构建工具备不少,好比Npm Script任务执行者、Grunt也是任务执行者、Gulp基于流的自动化构建工具、Fis3百度构建工具、Webpack打包模块化JavaScript
工具和Rollup模块打包工具。javascript
这里谈谈用得很频繁的webpack
构建工具。css
从最基本的概念开始了解:html
entry
是配置模块的入口,必填。前端
module.exports = {
entry: './path/to/my/entry/file.js'
}
复制代码
output
配置如何输出最终想要的代码。output
是一个object
,里面包含一系列的配置项。java
output.filename配置输出文件的名称,为string类型。node
output.path配置输出文件存放在本地的目录(路径),必须是string类型的绝对路径。react
path: path.resolve(__dirname, 'dist_[hash]')
复制代码
output.publicPath配置发布到线上资源的URL前缀,为string类型。默认为空字符串''
,即便用相对路径。jquery
好比须要将构建的资源上传到CDN服务上,以便加快网页的打开速度。配置代码以下:webpack
filename: '[name]_[chunkhash:8].js'
publicPath: 'https://cdn.example.com/assets/'
复制代码
发布到线上时候,HTML中引入的JavaScript
文件以下:git
<script src='https://cdn.example.com/assets/a_12345678.js'></script>
复制代码
线上出现404错误的时候,看下路径有没有错~
还有其余配置请看文档
module
配置如何处理模块。
module.rules配置模块的读取和解析规则,一般用来配置Loader
。其类型是一个数组,数组里每一项都描述了如何去处理部分文件。应用一项rules
时大体经过如下方式:
test
、include
、exclude
三个配置项来命中Loader
要应用规则的文件。use
配置项来应用Loader
,能够只应用一个Loader
或者按照从后往前的顺序应用一组Loader
,同时还能够给Loader
传入参数。Loader
执行顺序默认是从右往左执行,经过enforce
选项可让其中一个Loader
的执行顺序放在前面或者最后。module: {
rules: [
{
// 命中scss文件
test: /\.scss$/,
// 处理顺序从右往左
use: ['style-loader', 'css-loader', 'sass-loader'],
// 排除node_modules目录下的文件
exclude: path.resolve(__dirname, 'node_modules'),
}
]
}
复制代码
Loader须要传入多个参数的时候的例子:
use: [
{
loader:'babel-loader',
options:{
cacheDirectory:true,
},
// enforce:'post' 的含义是把该 Loader 的执行顺序放到最后
// enforce 的值还能够是 pre,表明把 Loader 的执行顺序放到最前面
enforce:'post'
},
// 省略其它 Loader
]
复制代码
module.noParse配置项可让webpack忽略对部分没采用模块化的文件的递归解析和处理,这样作有助于提升构建性能。好比:
module: {
noParse: (content) => /jquery|lodash/.test(content)
}
复制代码
module.rules.parser属性能够更细粒度的配置哪些模块须要解析,哪些不须要,和noParse
配置项的区别在于parser
能够精确到语法层面,而noParse
只能控制哪些文件不被解析。
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
parser: {
amd: false, // 禁用 AMD
commonjs: false, // 禁用 CommonJS
...
}
}
]
}
复制代码
Resolve
配置webpack如何寻找模块所对应的文件。Webpack
内置Javascript
模块化语法解析功能,默认会采用模块化标准里面约定好的规则去寻找,你也能够按照需求修改默认规则。
resolve.alias配置项经过别名来把原导入的路径映射成一个新的导入路径。以下:
resolve: {
alias: {
components: './src/components/'
}
}
复制代码
当你经过import Button from 'components/button'
导入时,实际上被alias
等价替换了import Button from './src/components/button'
。
resolve.modules配置webpack
去哪些目录下找第三方模块,默认只会去node_modules
目录下寻找。
resolve.enforceExtension若是配置为true
全部导入语句都必须带有后缀,例如开启前import './foo
能正常工做,开启后就必须写成import './foo.js'
。
Plugin
用于扩展Webpack
功能,各类各样的Plugin
几乎让Webpack
能够作任何构建相关的事情。
举个例子:
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
plugins: [
// 全部页面都会用到的公共代码提取到 common 代码块中
new CommonsChunkPlugin({
name: 'common',
chunks: ['a', 'b']
})
]
}
复制代码
在开发环境的时候使用。要配置DevServer
,除了在配置文件里面经过devServer
传入参数外,还能够经过命令行参数传入。
注意:只有在经过DevServer
去启动Webpack
时配置项文件里devServer
才会生效。
devServer.hot配置是否启用使用DevServer中提到的模块热替换功能。
devServer.host配置项用于配置 DevServer 服务监听的地址。
devServer.port配置项用于配置 DevServer 服务监听的端口,默认使用8080
端口。
devServer.https配置HTTPS协议服务。某些状况下你必须使用HTTPS,HTTP2 和 Service Worker 就必须运行在 HTTPS 之上。
devServer: {
https: true
}
复制代码
Webpack的运行是一个串行的过程,从启动到结束会执行如下流程:
Loader
对文件的转换操纵很耗时,须要让尽量少的文件被Loader
处理。
在使用Loader时能够经过test
、include
、exclude
三个配置项来命中Loader
要应用规则的文件。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src')
}
]
}
}
复制代码
resolve.modules
配置resolve.modules
用于配置webpack去哪些目录下寻找第三方模块。
resolve.modules
的默认值是['node_modules']
,含义是先去当前的目录下./node_modules
目录下去找想找的模块,以此类推,若是没有找到就去上一级目录../node_modules
中找,再没有去上上一级,以此类推...
若是知道安装的模块在项目的根目录下的./node_modules
时候,没有必要按照默认的方式一层层找:
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'node_modules')]
}
}
复制代码
resolve.alias
配置resolve.alias
配置项经过别名来把原导入路径映射成一个新的导入路径。能够减小耗时的递归解析操做。
module.noParse
配置module.noParse
配置项可让Webpack
忽略对部分没采用模块化的文件的递归解析处理,这样作的好处是能提升构建性能。
const path = require('path');
module.exports = {
module: {
// 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理
noParse: [/react\.min\.js$/],
}
}
复制代码
HappyPack
把任务分解成多个子进程并发执行,子进程处理完后再把结果发送给主进程。减小了总的构建时间。
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'happypack/loader?id=happyBabel',
exclude: /node_modules/
}
]
},
plugins: [
new HappyPack({
id: 'happyBabel',
loaders: [{
loader: 'babel-loader?cacheDirectory= true',
}],
// 共享进程池
threadPool: happyThreadPool,
// 容许happypack输出日志
verbose: true,
})
]
}
复制代码
例如:
module.export = {
watch: true,
watchOptions: {
// 监听到变化发生后会等300ms再去执行动做,防止文件更新太快致使从新编译频率过高
// 默认为 300ms
aggregateTimeout: 300,
// 判断文件是否发生变化是经过不停的去询问系统指定文件有没有变化实现的
// 默认每隔1000毫秒询问一次
poll: 1000
}
}
复制代码
因为保存文件的路径和最后编辑时间须要占用内存,定时检查周期检查须要占用CPU
以及文件I/O
,因此最好减小须要监听的文件数量和下降检查频率。
热替换就是当一个源码发生改变的时,只从新编译发生改变的模块,再用新输出的模块替换掉浏览器中对应的老模块。
开启热替换:
webpack-dev-server --hot
复制代码
区分开发环境和生产环境,进行不一样的构建~
CDN又叫内容分发网络,经过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。
CDN 实际上是经过优化物理链路层传输过程当中的网速有限、丢包等问题来提高网速的。
结合publicPath
来处理:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
output: {
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
publicPath: '//js.cdn.com/id/'
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: ['css-loader?minimize'],
publicPath: '//img.cdn.com/id/'
}),
}
]
},
plugins: [
new WebPlugin({
template: './template.html',
filename: 'index.html',
// 指定存放 CSS 文件的 CDN 目录 URL
stylePublicPath: '//css.cdn.com/id/',
}),
new ExtractTextPlugin({
// 给输出的 CSS 文件名称加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
]
}
复制代码
将多余的代码移除。
webpack --display-used-exports --optimize-minimize
复制代码
公共代码的提取。
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
new CommonsChunkPlugin({
// 从哪些 Chunk 中提取
chunks: ['a', 'b'],
// 提取出的公共部分造成一个新的 Chunk,这个新 Chunk 的名称
name: 'common'
})
复制代码
对于采用单页应用做为前端架构的网站来讲,会面临一个网页须要加载的代码量很大的问题,由于许多功能都作到了一个HTML里面,这会致使网页加载缓慢、交互卡顿、用户体验将很是糟糕。
致使这个问题的根本缘由在于一次性的加载全部功能对应的代码,但其实用户每一阶段只可能使用其中一部分功能。 因此解决以上问题的方法就是用户当前须要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载。
Webpack 内置了强大的分割代码的功能去实现按需加载。好比:
main.js
文件,网页会展现一个按钮main.js
文件中只包含监听按钮事件和加载按需加载的代码。show.js
文件,加载成功后再执行show.js
里的函数。main.js中:
window.document.getElementById('btn').addEventListener('click', function () {
// 当按钮被点击后才去加载 show.js 文件,文件加载成功后执行文件导出的函数
import(/* webpackChunkName: "show" */ './show').then((show) => {
show('Webpack');
})
});
复制代码
show.js中:
module.exports = function (content) {
window.alert('Hello ' + content);
};
复制代码
代码中最关键的一句是import(/* webpackChunkName: "show" */ './show')
,Webpack 内置了对import(*)
语句的支持,当 Webpack 遇到了相似的语句时会这样处理:
./show.js
为入口新生成一个Chunk
;import
所在语句时才会去加载由Chunk
对应生成的文件。import
返回一个Promise
,当文件加载成功时能够在Promise
的then
方法中获取到show.js
导出的内容。在工做中具体使用到的时候再按须要进行更改配置项啦~