# 前端工程化篇

# 1. webpack 常用的几个对象及解释(中级)

  • entry 入口文件

  • output 输出文件

    • 一般配合node的path模块使用

      // 入口文件
          entry:"./src/index.js",
          output:{
              // 输出文件名称
              filename:"bundle.js",
              // 输出的路径(绝对路径)
              path:path.resolve(__dirname,"dist") //利用node模块的path 绝对路径
          },
          // 设置模式
          mode:"development"
      
      
  • mode 设计模式

  • module(loader)

    • 里面有一个rules数组对某种格式的文件进行转换处理(转换规则)

    • use数组解析顺序是从下到上逆序执行的

      module:{
             // 对某种格式的文件进行转换处理(转换规则)
             rules:[
                 {
                     // 用到正则表达式
                     test:/\.css$/,      //后缀名为css格式的文件
                     use:[
                         // use数组解析顺序是从下到上逆序执行的
                         // 先用css-loader 再用style-loader
                         // 将js的样式内容插入到style标签里
                         "style-loader",
                         // 将css文件转换为js
                         "css-loader"
                     ]
                 }
             ]
         }
       
      // -----------------------------------------------------vue的
      module.exports={
          module:{
              rules:[
                  {
                      test: /\.vue$/,
                      use:["vue-loader"]
                  }
              ]
          }
      }
      
  • plugin

    • 插件配置

      const uglifyJsPlugin = reqiure('uglifyjs-webpack-plugin')
       
      module.exports={
      	plugin:[
      		new uglifyJsPlugin()	//丑化
      	]
      }
      
  • devServer

    • 热更新

      devServer:{
          // 项目构建路径
          contentBase:path.resolve(__dirname,"dist"),
          // 启动gzip亚索
          compress:true,
          // 设置端口号
          port:2020,
          // 自动打开浏览器:否
          open:false,
          //页面实时刷新(实时监听)
          inline:true
      }
      
  • resolve

    • 配置路径规则

    • alias 别名

      module.exports= {
      	resolve:{
      		//如果导入的时候不想写后缀名可以在resolve中定义extensions
      		extensions:['.js','.css','.vue']
      		//alias:别名
      		alias:{
      			//导入以vue结尾的文件时,会去寻找vue.esm.js文件
      			'vue$':"vue/dist/vue.esm.js"
      		}
      	}
      }
      
  • babel(ES6转ES5)
    • 下载插件babel-loader,在module(loader)中配置

# 2. loader 和 plugin 的区别是什么?(中级)

  • loader

    • loader 是用来解析非 js 文件的,因为 Webpack 原生只能解析 js 文件,如果想把那些文件一并打包的话,就需要用到 loader,loader 使 webpack 具有了解析非 js 文件的能力。
  • plugin

    • 用来给 webpack 扩展功能的,可以加载许多插件。

# 3. webpack 与 grunt、gulp 的不同?(中级)

三者都是前端构建工具,grunt 和 gulp 在早期比较流行,现在 webpack 相对来说比较主流,不过一些轻量化的任务还是会用 gulp 来处理,比如单独打包 CSS 文件等。

grunt (opens new window)gulp (opens new window) 是基于任务和流(Task、Stream)的。类似 jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个 web 的构建流程。

webpack 是基于入口的。webpack 会自动地递归解析入口所需要加载的所有资源文件,然后用不同的 Loade r来处理不同的文件,用 Plugin 来扩展 webpack 功能。

所以总结一下:

  • 从构建思路来说
gulp和grunt需要开发者将整个前端构建过程拆分成多个`Task`,并合理控制所有`Task`的调用关系
webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工
  • 对于知识背景来说 gulp 更像后端开发者的思路,需要对于整个流程了如指掌 webpack 更倾向于前端开发者的思路。

# 4. 与 webpack 类似的工具还有哪些?谈谈你为什么最终选择(或放弃)使用webpack?(中级)

同样是基于入口的打包工具还有以下几个主流的:

从应用场景上来看:

  • webpack适用于大型复杂的前端站点构建。
  • rollup适用于基础库的打包,如vue、react。
  • parcel适用于简单的实验性项目,他可以满足低门槛的快速看到效果。

由于parcel在打包过程中给出的调试信息十分有限,所以一旦打包出错难以调试,所以不建议复杂的项目使用parcel

# 5. 有哪些常见的 Loader?他们是解决什么问题的?(中级)

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件。
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去。
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试。
  • image-loader:加载并且压缩图片文件。
  • babel-loader:把 ES6 转换成 ES5。
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性。
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码。

# 6. 有哪些常见的 Plugin?他们是解决什么问题的?(中级)

  • define-plugin:定义环境变量。
  • commons-chunk-plugin:提取公共代码。
  • uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码。

# 7. 如何利用 webpack 来优化前端性能?(提高性能和体验)(高级)

用 webpack 优化前端性能是指优化 webpack 的输出结果,让打包的最终结果在浏览器运行快速高效。

  • 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用 webpack 的UglifyJsPluginParallelUglifyPlugin来压缩 JS 文件, 利用cssnano(css-loader?minimize)来压缩 css。
  • 利用 CDN 加速。在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径。可以利用 webpack 对于output参数和各loader的publicPath参数来修改资源路径。
  • 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动 webpack 时追加参数--optimize-minimize来实现。
  • 提取公共代码。

# 8.怎么配置单页应用?怎么配置多页应用?(高级)

单页应用可以理解为 webpack 的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述。

多页应用的话,可以使用 webpack 的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:

  • 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套 css 样式表。

  • 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置。

# 9.npm 打包时需要注意哪些?如何利用 webpack 来更好的构建?(高级)

Npm是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。你可能只是 JS 模块的使用者,但是有些情况你也会去选择上传自己开发的模块。 关于 NPM 模块上传的方法可以去官网 (opens new window)上进行学习,这里只讲解如何利用 webpack 来构建。

NPM 模块需要注意以下问题:

  1. 要支持 CommonJS 模块化规范,所以要求打包后的最后结果也遵守该规则。
  2. Npm 模块使用者的环境是不确定的,很有可能并不支持 ES6,所以打包的最后结果应该是采用ES5编写的。并且如果 ES5 是经过转换的,请最好连同 SourceMap 一同上传。
  3. Npm 包大小应该是尽量小(有些仓库会限制包大小)。
  4. 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。
  5. UI 组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里。

基于以上需要注意的问题,我们可以对于 webpack 配置做以下扩展和优化:

  1. CommonJS 模块化规范的解决方案: 设置output.libraryTarget='commonjs2'使输出的代码符合CommonJS2 模块化规范,以供给其它模块导入使用。
  2. 输出ES5 代码的解决方案:使用babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map'输出 SourceMap 以发布调试。
  3. Npm 包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5 代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件。
  4. 不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉 webpack 哪些模块不需要打包。
  5. 对于依赖的资源文件打包的解决方案:通过css-loaderextract-text-webpack-plugin来实现,配置如下:
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        // 增加对 CSS 文件的支持
        test: /\.css/,
        // 提取出 Chunk 中的 CSS 代码到单独的文件中
        use: ExtractTextPlugin.extract({
          use: ['css-loader']
        }),
      },
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      // 输出的 CSS 文件名称
      filename: 'index.css',
    }),
  ],
};