构建工具篇

webpack是事实上的前端打包标准,使用较广泛,相比其他打包工具webpack更是面试中的热点

用的webpack3还是版本4,知道有哪些改进嘛

webpack4是18年8月25号发布的,相比webpack3主要以下大改动:

  • node环境升级,不再支持node4.0及之前版本,最低node版本6.11.5

  • 配置增加了mode:production/development/none,必须指定其中一个,在不同的mode下开启了一些默认的优化手段;

  • 不再需要某些plugin,改为在对应生产或者开发模式下默认打开

详细改进:webpack Release v4.0.0

loader和plugin的不同

  • loader能让webpack处理不同的文件,然后对文件进行一些处理,编译,压缩等,最终一起打包到指定文件中(比如loader可以将sass,less文件写法转换成css,而不需要在使用其他转换工具),loader本身是一个函数,接收源文件作为参数,返回转换的结果

// build/webpack.base.conf.js
module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
    ]
  },
  • plugins则用于执行广泛的任务,从打包,优化,压缩,一直到重新定义环境中的变量,接口很强大,主要用来扩展webpack的功能,可以实现loader不能实现的更复杂的功能

// build/webpack.prod.conf.js
plugins: [
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
]

有手动编写过loader和plugin,说一下思路

loader能把源文件经过转化后输出新的结果,一个loader遵循单一职责原则,只完成一种转换,然后链式的顺序去依次经过多个loader转换,直到得到最终结果并返回,所以在写loader时要保持其职责的单一性,同时webpack还提供了一些API供loader调用

// 一个简单的loader例子
function replace(source) {
    // 使用正则把 @require '../style/index.css' 转换成 require('../style/index.css');  
    return source.replace(/(\/\/ *@require) +(('|").+('|")).*/, 'require($2);');
}
module.exports = function (content) {
    return replace(content);
};

开发plugin中最常用的两个对象是Compiler和Compilation,他们是plugin和webpack之间的桥梁

  • Compiler对象包含了webpack环境的所有配置信息,包含options,loaders,plugins这些信息,这个对象在Webpack启动时候被实例化,它是全局唯一的

  • Compilation对象包含了当前的模块资源,编译生成资源,变换的文件等。当webpack以开发模式运行时,每当检测到一个文件变化,一个新的Compilation将会被创建

// 最基础的plugin代码
class BasicPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options){ }
    // Webpack会调用BasicPlugin实例的apply方法给插件实例传入compiler对象
    apply(compiler){
        conpiler.plugin('compilation',function(compilation){ })
    }
}
module.exports = BasicPlugin;

webpack如何配置单页和多页应用

单页应用即为webpack的标准模式,直接在entry中指定单页面应用的入口即可:

module.exports = {
  entry: {
    app: './src/main.js'
  },
}

多页面应用可以考虑使用webpack的AutoWebPlugin来完成简单的自动化构建,前提是项目目录结构要符合预先设定的规范

├── pages
│   ├── index
│   │   ├── index.css // 该页面单独需要的 CSS 样式
│   │   └── index.js // 该页面的入口文件
│   └── login
│       ├── index.css
│       └── index.js
├── common.css // 所有页面都需要的公共 CSS 样式
├── google_analytics.js
├── template.html
└── webpack.config.js

webpack如何做到热更新

webpack热更新(Hot Module Replacement),缩写为HMR,实现了不用刷新浏览器而将新变更的模块替换掉旧的模块,原理如下:

server端和client端都做了处理:

  • webpack监听到文件变化,重新编译打包,webpack-dev-server和webpack之间接口交互(主要是webpack-dev-middleware调用webpack暴露的API对代码进行监控,并告诉webpack将打包后代码保存到内存中)

  • 通过sockjs(webpack-dev-server的依赖)在浏览器和服务器之间建立 一个websocket长连接,将webpack编译打包各阶段的信息告知浏览器端

  • webpack根据 webpack-dev-server/client 传给它的信息以及 dev-server的配置决定是刷新浏览器还是进⾏模块热更新,如果是模块热更新继续执行,否者刷新浏览器

  • HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上⼀步传递给他的新模块的 hash 值,它通过JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回⼀个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码

  • HotModulePlugin 将会对新旧模块进⾏对⽐,决定是否更新模块

  • 当 HMR 失败后,回退到 live reload 操作,也就是进⾏浏览器刷新来获取最新打包代码

参考:Webpack HMR 原理解析

webpack的构建流程(原理)是怎样的

webpack的构建流程是一个串行的过程,从启动到结束依次执行如下:

  • ① 初始化参数:从配置文件和shell语句中读取与合并参数,得出最终的参数

  • ② 开始编译:用上一步得到的参数初始化 Compiler对象,加载所有配置的插件,通过执行对象的run方法开始执行编译

  • ③ 确定入口:根据配置中的entry找出所有的入口文件

  • ④ 编译模块:从入口文件出发,调用所有配置的loader对模块进行"加载",再找出该模块依赖的模块,递归此步骤知道所有入口依赖的文件都经过处理结束

  • ⑤ 完成编译模块:处理结束,得到了每个模块被"加载"之后的最终内容以及他们之间的依赖关系

  • ⑥ 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,再将每个chunk转换成一个单独的文件加入输出列表中

  • ⑦ 输出完成:确定输出内容之后,根据配置确定输出的路径和⽂件名,写⼊到⽂件系统

如何用webpack优化前端性能

  • 压缩代码:比如利用UglifyJsPlugin来对js文件压缩

  • CDN加速:将引用的静态资源修改为CDN上的路径。比如可以抽离出静态js,在index利用CDN引入;利⽤webpack对于 output 参数和各loader的publicPath参数来修改资源路径

  • 删除Tree Shaking:将代码中永远不会走到的片段删除。可以通过在启动webpack时追加参数 --optimize-minimize 来实现

  • 按照路由拆分代码,实现按需加载,提取公共代码

  • 优化图片,对于小图可以使用 base64 的方式写入文件中

Last updated