# Task-02-02 **Repository Path**: fishlyn/Task-02-02 ## Basic Information - **Project Name**: Task-02-02 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-07-05 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Part2 模块二作业 ## 一、简答题 **1、 Webpack 的构建流程主要有哪些环节?如果可以请尽可能详尽的描述 Webpack 打包的整个过程。** 答:1、Webpack 会根据配置文件找到整个项目的入口文件 ​ 2、根据入口文件所引入的模块,通过递归的方式,逐层寻找,直到找到所有的相互依赖的模块,形成依赖关系树 ​ 3、然后会使用相应的 Loader 对不同的文件格式,解析代码,转换代码,编译代码,最后输出打包结果 **2、 Loader 和 Plugin 有哪些不同?请描述一下开发 Loader 和 Plugin 的思路。** 答:Loader:加载器,用于加载资源文件,并对文件进行处理,比如进行编译,压缩等,最后将文件打包到 bundle 中,而 Loader 是一种管道的机制,可以依次执行多个 Loader,最终将结果返回 ​ Plugin:插件,相比于 Loader 只在加载文件的时候使用,plugin 在 webpack 整个生命周期中都可以使用,通过 webpack 的钩子机制,在不同的钩子函数上挂载相应的 plugin 任务 **开发一个 Loader:** 1、Loader 的原理是将读取到的文件数据作为参数,通过函数处理之后,返回可执行的 Javascript 代码,Webpack 会根据代码拼接的方式,将这段代码打包到模块中执行 2、所以我们可以通过 module.exports 的方式导出一个函数,这个函数会传入根据 rules 匹配到的文件的数据,然后在函数内部进行处理 3、将处理后的数据javascript的方式返回 **开发一个 Plugin:** 1、确定需要在 webpack 的哪个生命周期中进行数据的处理 2、查阅 webpack 的生命周期文档,找到合适的钩子函数,通过 tap() 方法,执行自定义的任务 3、执行完自定义的任务之后,将最终处理的数据,重新赋值给此次打包的上下文对象,是 webpack 可以读取到处理后的数据,从而自定义打包结果 ## 二、编程题 1、使用 Webpack 实现 Vue 项目打包任务 答: 拿到项目后,先对项目执行 `yarn` ,以便初始化项目,并安装项目默认依赖插件 安装 webpack `yarn add webpack webpack-cli --dev` 在 webpack 4.0 之后 webpack-cli 被单独提取出来了,所以需要单独安装 webpack 安装完成后就可以编写配置文件对项目进行打包了 **一、使用 Loader 对文件编译** webpack 只支持 js 文件的编译,其他格式的文件编译需要通过加载器进行编译,根据项目的架构,选用以下几个加载器 * **less-loader** **css-loader** **style-loader** 用于对 .less 文件 .css 文件进行编译,以及将css样式内嵌到html页面执行 * **vue-loader** 用于对 .vue 文件进行编译 * **file-loader** **url-loader** 文件资源加载器,可以加载图片资源,url-loader 可以将图片转成 base64 格式嵌入到代码中 ```js // webpack.config.js const path = require('path') module.exports = { mode: 'none', // 暂时不对打包结果做处理 entry: './src/main.js', // 入口文件 output: { // 打包结果输出目录 filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, module: { // 配置加载器规则 rules: [ { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.vue$/, use: 'vue-loader' }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.(png|jpe?g|gif)$/, use: 'url-loader' } ] } } ``` **二、解决 vue 文件内的语法编译的问题** vue 文件是由 template,script,style 三个语法模块组成的,所以需要分别给这三个模块提取出来并根据配置的规则进行编译 * **vue-template-compiler** 编译 vue 文件的 template 模板标签,安装之后执行 vue-loader 之后会自动调用将 template 模板内的标签转换成 html * vue-loader/lib/plugin 该目录下的插件可以根据rules下配置的加载器,自动将 vue 的 script,style 下的进行编译 ```js // webpack.config.js const path = require('path') const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ ... ] }, plugins: [ new VueLoaderPlugin() ] } ``` **三、将 bundle.js 挂载到 html 下** 经过以上的操作,webpack 将项目的所有文件打包到了 bundle.js 了,此时我们将 bundle.js 引入到 html,就能执行了 为了能自动生成 html ,以及根据模板生成文件,我们可以使用 html-webpack-plugin 插件 但是添加编译模板的插件并执行之后会发现,模板文件里的模板语法有全局变量的存在且没有赋值,所以我们可以通过 webpack 的 DefinePlugin 定义全局的变量 ```js // webpack.config.js const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ ... ] }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ // 通过模板生成 html template: './public/index.html' }), new webpack.DefinePlugin({ BASE_URL: '"./"' }) ] } ``` **四、处理图片不显示的问题** 经过上面的处理,会自动根据模板生成 index.html 文件了,但是在浏览器打开之后发现图片不显示 原因是加载器是管道执行的,当前 Loader 需要导入前一个 Loader 导出的数据,此时,因为规范的不一致,有些 Loader 使用的是 CommonJS 的标准导入数据,有些是使用 ES Module 的标准,所以产生读取不到的问题 而 vue-loader 加载模块是以 CommonJS 标准的,所以需要将 file-loader/url-loader 的 ES Module 导出方式改成 CommonJS 的 ```js // webpack.config.js const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ ... { test: /\.(png|jpe?g|gif)$/, use: { loader: 'url-loader', options: { esModule: false // 关闭 ES Module 标准 } } } ] }, plugins: [ ... ] } ``` **五、处理静态资源** 项目内还有不需要做任何处理的静态资源,这时候可以使用 copy-webpack-plugin 插件将静态资源拷贝到生产目录, 以及为了每次打包生产目录都是最新的代码,在生成前应该先删除原先的目录再生成新的,可以使用 clean-webpack-plugin 插件进行处理 ```js const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ ... ] }, plugins: [ ... new CleanWebpackPlugin(), new CopyWebpackPlugin({ // 用法和老师教的不同,可能使用了最新的版本,更改了用法 patterns: [ { from: 'public/*ico', // 只需要赋值 ico 文件,忽略 html 文件 platten: true // 忽略目录,只复制文件名 } ] }) ] } ``` 到此为止,webpack 基本打包完成,项目可以正常运行,资源文件也能正常加载了 **六、使用开发服务器** 为了能使每次编译完成保存之后,能实时预览到效果,可以通过将代码运行在本地服务器的方式,使用 webpack-dev-server 插件可以实现 安装好插件后,直接运行 `yarn webpack-dev-server --open` 可以直接打开浏览器预览效果,webapck-dev-server 生成的项目是在内存中的,所以每次修改编译的速度会比较快 ```js const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, devServer: { contentBase: './public' // 配置开发服务器的静态文件路径 }, module: { rules: [ ... ] }, plugins: [ ... ] } ``` **七、使用 ESLint 进行代码检查** 为了使代码风格统一,可以使用 eslint-loader 加载器对 js 文件的代码进行风格的统一 在使用 ESLint 之前,需要安装 * `yarn add eslint eslint-loader --dev` * 执行` yarn eslint --init` 初始化 .eslintrc.js ```js const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'js/bundle.js', path: path.join(__dirname, 'dist') }, devServer: { ... }, module: { rules: [ { test: /\.js$/, use: 'eslint-loader', exclude: /node_module/, // 不检查 node_module 下的文件 enforce: 'pre' // 在匹配到 js 文件时,优先使用 eslint-loader } ] }, plugins: [ ... ] } ``` **八、优化项目** 最后根据自己的需求是否使用 webpack 的某些功能 * 是否需要使用 babel 转换 es6 代码 * 是否需要配置 devtool ,以便使用 source-map 的功能 * 是否开启 HMR ,以便进行模块热替换 * 是否需要提取 css 文件,进行 css 的代码压缩 * 是否需要将打包结果分包 * 是否需要配置 Hash 缓存 * 是否需要将图片转成 base64 * 是否配置不同环境下的配置文件 * ......