Webpack从2015年9月第一个版本横空初始至今已逾2载。它的出现,颠覆了一大批主流构建如Ant、Grunt和Gulp等等。腾讯NOW直播IVWEB团队之前一直采用Fis构建,本篇文章主要介绍从Fis迁移到webpack遇到的问题和背后的黑科技,内容包括inline-resource、多页面构建、资源压缩、文件hash、文件目录规则等等。

为什么要迁移至webpack?

有两个层面的原因:

  • 首先webpack的社区生态火爆,插件齐全并且维护更新的很频繁,遇到了问题,比较容易解决。
  • webpack里面有happypack多实例构建方案、code spliting按需加载文件等方案, 可以有效的进行打包构建持续优化, 这些在Fis里面是缺少的。

区分构建的开发or生产环境?

  "scripts": {
"dev": "cross-env NODE_ENV=dev nodemon --watch webpack.config.js --exec \"webpack-dev-server --config webpack.config.js --env development\" --progress --colors",
"build": "webpack --config webpack.config.js --env production --progress --colors",
...
},

通过在package.json中注入环境变量的方式,注入NODE_ENV=dev代表开发环境,默认为生产环境。这里使用cross-env的原因是:windows下 在package.json中直接使用 NODE_ENV=dev 不生效,需写成 set NODE_ENV=dev,cross-env的写法兼容各个操作系统。

资源内联 (inline-resource)

inline-resource的好处是可以减少css,js等的请求数,同时html加载的时候即可同时加载了这些内联的css、js等静态资源,可以有效的减少白屏或者页面闪动的问题。

这里的内联分为2种,一种是静态的html片段,css,js等,这些资源一开始就存在项目的某个目录下;另一种是构建过程中动态生成的css,js文件。

对于html的内联,写法如下:

  ${require('raw-loader!../src/assets/inline/meta.html')}

对于js的内联,需要增加babel-loader将ES6的语法进行转换,避免浏览器直接解析导致报错。写法如下:

<script>${require('raw-loader!babel-loader!../src/node_modules/@tencent/report-whitelist/lib/index.js')}</script>

说明:不能将html-loader和html-webpack-plugin同时使用,html-loader会导致默认的ejs模板引擎语法解析实效,造成 ${} 和 <% = %>等语法不生效

上面讲述了如何内联静态的资源文件,那么如何内联构建过程中动态生成的资源文件呢?这里需要借助html-webpack-inline-source-plugin来增强html-webpack-plugin的功能。比如:将构建过程中生成的css文件inline到html模板里面去。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin'); new HtmlWebpackPlugin({
inlineSource: isDev ? undefined : '\\.css$',
template: __dirname + '/template/index.tmpl.html',
filename: 'activity.html',
inject: true,
}),
new HtmlWebpackInlineSourcePlugin(),
...

上面这段代码,html-webpack-plugin本身并不具备inlineSource的属性。引入了html-webpack-inline-source-plugin之后,就可以通过inlineSource属性来匹配哪些文件需要动态的内联进html模板文件中了。

多页面构建

多页面构建,或者称为通配(wildcards)构建。即需要构建的页面数量是不确定的,可能A业务有3张页面,B业务有5张页面。因此,我们不能把entry写死了:

entry: {
activity: './src/pages/activity/init.js',           // 深海寻宝活动首页
my-reward: './src/pages/my-reward/init.js',         // 我的奖励
exchange: './src/pages/exchange/init.js'             // 线下兑换奖品
},

为什么上面的写法不可取呢?很明显:上面的写法把entry写死了,这并不通用。后面如果产品需求发生改变,需要新增一张页面,就需要手动修改构建脚本。我们需要的entry是:'./src/pages/**/init.js',它能够像一些linux的命令,具备匹配某个规则的所有结果的能力。这里的思路是借助glob,达到动态entry的目的。

entry: glob.sync('./src/pages/**/init.js'),

在webpack构建中,一个页面需要一个与之对应的HtmlWebpackPlugin实例,N个页面需要N个与之对应的HtmlWebpackPlugin。此处需要动态的设置HtmlWebpackPlugin的实例个数。

const newEntry = {};

Object.keys(config.entry).map((index) => {
const entry = config.entry[index];
const match = entry.match(/\/pages\/(.*)\/init.js/);
const pageName = match && match[1]; newEntry[pageName] = entry; config.plugins.push(
new HtmlWebpackPlugin({
inlineSource: isDev ? undefined: '\\.css$',
template: __dirname + '/template/index.tmpl.html',
filename: `${pageName}.html`,
chunks: [pageName],
inject: true
})
);
});
config.entry = newEntry;

html、css和js压缩

对于html文件里面的内容压缩可以通过给html-webpack-plugin设置minify参数,html-webpack-plugin的压缩配置其实是直接集成了 html-minifier,因此minify的参数设置可以直接参考html-minifier的文档。

config.plugins.push(
new HtmlWebpackPlugin({
inlineSource: isDev ? undefined: '\\.css$',
template: __dirname + '/template/index.tmpl.html',
filename: `${pageName}.html`,
chunks: [pageName],
inject: true,
minify: {
           minifyJS: true,           // 仅压缩内联在html里面的js
           minifyCSS: true,         // 仅压缩内联在html里面的css
           html5: true,             // 以html5的文档格式解析html的模板文件
           removeComments: false,   // 不删除Html文件里面的注释
           collapseWhitespace: true, // 删除空格
           preserveLineBreaks: false // 删除换行
       }
})
);

设置了上面的minify参数后,看到生成的html文件的内容全部在1行上,需要注意的是:minifyJS和minifyCSS只会压缩内联在这个html文件的css和js内容,对于单独的css文件和js文件并不会压缩。 那么打包出来的css和js文件如何压缩呢?

对于css文件压缩,直接开启css-loader的压缩参数参数minimize为true即可:

{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: "css-loader",
               options: {               // 设置css-loader的minimize参数为true
                 minimize: true
}
},
{
loader: "sass-loader"
}
]
})
},

css-loader开启压缩可能会报错 Module build failed: BrowserlistError: unkonwn version 61 and _chr,解决办法:

$ npm i caniuse-db —save    #更新caniuse-db到最新版本

对于js文件的压缩,可以通过引入 webpack 内置的 UglifyJsPlugin:

const webpack = require('webpack');
plugins: [
...
new webpack.optimize.UglifyJsPlugin(),
...
],

文件Hash

每次功能发布上线,都需要重新构建一次源代码,生成一个新的文件版本列表。此处文件Hash的方式就是一种版本管理的方式,发布时替换有变化的版本的文件,达到增量更新的效果。此处Hash策略是:根据文件内容进行hash,取8位。

JS文件:

output: {
   filename: '[name]_[chunkhash:8].js',     // 进行js脚本hash
   path: path.resolve(__dirname, 'public/'),
publicPath: isDev ? '/' : cdnUrl + '/',
},

Css文件:

plugins: [
new CleanWebpackPlugin(['./public']),
   new ExtractTextPlugin('[name]_[contenthash:8].css'), // css文件hash
   new webpack.optimize.UglifyJsPlugin(),
...
]

Img文件:

rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader: 'file-loader',
options: {
               name: '[name]_[hash:8].[ext]',   // img文件hash
           }
}
},
...
]

多终端适配

开发过程中,不同分辨率的浏览器适配是个让前端开发者头疼的问题。手淘的rem方案完美解决了这个问题,它的核心思想是页面加载时动态设置body的font-size值和rem和px转换的单位。

为了不改变编程习惯,开发过程中仍然使用px单位来作为基础布局长度单位,以750px宽度的视觉稿作为基准,设置rem和px的转换单位为1rem=75px。那么px2rem如何集成进webpack中呢?首先增加css的解析px2rem-loader,然后在html头部引入lib-flexible文件。

{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: "css-loader"
},
{
               loader: "px2rem-loader", // 增加px2rem-loader,并且设置rem单位为75px
               options: {
remUnit: 75
}
},
{
loader: "sass-loader"
}
]
})
},

其它feature

  • 开发环境支持WDS: webpack3.x版本自带webpack-dev-server,开发环境中开启WDS。这样依赖的文件发生变化后,会自动增量构建并且刷新浏览器
  • 支持HMR: webpack.config.js文件内容变化后,会触发热更新逻辑,此处通过nodemon来守护webpack的构建进程,eg:
  "scripts": {
"dev": "cross-env NODE_ENV=dev nodemon --watch webpack.config.js --exec \"webpack-dev-server --config webpack.config.js --env development\" --progress --colors"
...
},

由于篇幅原因,关于webpack的打包优化将会用另外一篇文章介绍,敬请期待~

参考文档

打个广告

Fis3构建迁移Webpack之路的更多相关文章

  1. Fis3的前端模块化之路[基础篇]

    Fis3版本:v3.4.22 fis3是一个构建工具 解决前端开发中自动化工具.性能优化.模块化框架.开发规范.代码部署.开发流程等问题. 安装 npm install -g fis3 运行 fis3 ...

  2. 深入浅出的webpack构建工具---webpack基本配置(一)

    深入浅出的webpack构建工具---webpack基本配置(一) 阅读目录 一:webpack入门构建: 1. 安装webpack到全局 2. 安装webpack到本项目. 3. 如何使用webpa ...

  3. 走向Node与Webpack 之路 - CommonJS 模块化

    走向Node与Webpack 之路 - CommonJS 模块化 1. 参考资料 JavaScript 标准参考教程(alpha) CommonJS规范(推荐 - 阮一峰写的) 官方网站 (看半天,不 ...

  4. 前端构建工具 webpack

    一.自我初级认知  (是什么?     能干什么,有卵用?       有选择为什么要选你?(比较优势在哪) )     适合的才是最好的 模块打包器(module bundler)     根据项目 ...

  5. 【前端构建】WebPack实例与前端性能优化

    计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给前端优化工作带来的便利. 壹 | Fisrt 曾几 ...

  6. 利用fis3构建前端项目工程

    FIS3是国内百度公司产出的一款前端工程构建工具,FIS3可以解决前端工程中性能优化.资源加载(异步.同步.按需.预加载.依赖管理.合并.内嵌).模块化开发.自动化工具.开发规范.代码部署等问题,首先 ...

  7. 前端自动化构建工具webpack (一)之webpack安装 和 设置webpack.confi.js

    目的:  模块化开发,组件化开发,让代码符合开发规范,更高效 webpack作用:从pack,就知道这是包的意思,比较专业点叫做前端自动化打包构建工具,代码错误检查,预处理,文件合并(打包),代码压缩 ...

  8. 深入浅出的webpack4构建工具---webpack+vue+router 按需加载页面(十五)

    1. 为什么需要按需加载? 对于vue单页应用来讲,我们常见的做法把页面上所有的代码都打包到一个bundle.js文件内,但是随着项目越来越大,文件越来越多的情况下,那么bundle.js文件也会越来 ...

  9. FIS3 构建 工程化

    1.安装 npm install -g fis3 //插件 npm install -g fis3-hook-relative npm install -g fis3-preprocessor-aut ...

随机推荐

  1. Android性能优化-减小图片下载大小

    原文链接 https://developer.android.com/topic/performance/network-xfer.html 内容概要 理解图片的格式 PNG JPG WebP 如何选 ...

  2. WPF应用程序exe接收参数

    using System;using System.ServiceProcess; namespace GoShopService{    public partial class Service1 ...

  3. docker安装hive笔记

    前两篇文章介绍了docker的基本命令如何安装hadoop 那么大家会比较了解docker的基本语法的安装过程.那么咱们今天来一起安装一下hive. 安装 1.下载gitHub,地址:https:// ...

  4. 【Java】MyBatis与Spring框架整合(一)

    本文将利用 Spring 对 MyBatis 进行整合,在对组件实现解耦的同时,还能使 MyBatis 框架的使用变得更加方便和简单. 整合思路 作为 Bean 容器,Spring 框架提供了 IoC ...

  5. 【转】《iOS7 by Tutorials》系列:iOS7的设计精髓(下)

    四.聚焦于内容 在iOS7里,强调的不是眼花缭乱的装饰效果,而是最重要的内容本身. 下面我们来探讨这个主题: 1.删除不必要的内容 伟大的设计更多是减法和加法的组合. 虽然很酷的想法是很重要,但还有更 ...

  6. zabbix监控k8s出现的pod error status

    配置zabbix客户端配置文件 vim /etc/zabbix/zabbix_agentd.conf 添加  Include=/etc/zabbix/zabbix_agentd.d/ #!/bin/b ...

  7. C#通过DSOFile读取与修改文件的属性

    搜了一圈用C#读取与修改文件属性的文章,结果几乎找不到- -: 偶然间看到一个DSOFile工具,然后找到了对该工具进行详细讲解的一篇文章:<DSOfile,一个修改windows系统文件摘要的 ...

  8. Java socket保存示例(不使用base64)解决中文乱码问题

    MultiThreadServer.java package com.my.nubase64; import java.io.BufferedReader; import java.io.Buffer ...

  9. python修饰器(装饰器)以及wraps

    Python装饰器(decorator)是在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼. 装饰器的引入 初期及问题的诞生 假如现在在一个公司,有A B C三个业务部门,还有S一 ...

  10. golang:mime.Decode、mime.DecodeHeader

    最近在做邮件解析的相关工作,在使用mime.Decode/mime.DecodeHeader时有些疑问. 有些搞不懂mime.Encode和mime.EncodeHeader的区别.