模块化开发与规范化标准

一、简答题

1、Webpack 的构建流程主要有哪些环节?如果可以请尽可能详尽的描述 Webpack 打包的整个过程。

大致分成六大部分

  • Entry:是webpack的入口,这是webpack执行构建的第一步,可以理解为输入
  • Modules:模块,在webpack中的一切皆是模块,一个模块就是一个文件。webpack会从enrtry开始递归找出所有的递归模块
  • Chunk:代码块,一个Chunk由多个模块组成,它是用来代码的合并和分割
  • Loader:模块转换器,用于将模块的原内容按照需求转换成新内容
  • Plugin:扩张插件,在webpack构建过程的特定时机注入扩展逻辑,用来改变或优化构建结果
  • Output: 输出结果,源码在webpack中经过一系列处理后而得出的最终结果

    webpack打包的整个流程

  1. Webpack在启动后,会从Entry开始,递归解析Entry依赖的所有Module,每找到一个Moudle,就会根据Moudle.rules里配置的Loader规则进行相应的转换处理
  2. 对Module进行转换后,再解析出当前Module依赖的Module,得到每个Module被转换或者编译后的最终内容以及他们之间的依赖关系
  3. 这些Module会以为Entry为单位进行分组,即为一个Chunk。因此一个Chunk就是一个Entry及其所有依赖的Module合并的结构,再把每个Chunk转接成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  4. 在确定好输出内容后,根据配置确定输出的路径和文件名,将所有chunk转换成文件输出Output

    2、Loader 和 Plugin 有哪些不同?请描述一下开发 Loader 和 Plugin 的思路。

    Loader和Plugin不同点:

  • Loader专注实现资源模块的转换和加载(编译转换代码、文件操作以及代码检查)
  • Plugin解决其他自动化工作(打包前清除dist目录、拷贝静态文件、压缩代码等)

    开发Loader思路:

  • 在项目根目录下新建test.loader.js

  • 这个文件需要导出一个函数,这个函数就是我们的loader对所加载到的资源的处理过程
  • 函数输入时”加载到的资源”,输出为”加工过的结果”
  • 输出结果可以有两种形式:1⃣️输出标准的JS代码,让打包结果的代码能正常运行;2⃣️输出的处理结果,继续交给下一个loader进一步处理成JS代码
  • 在webpack.config.js中使用loader,配置module.rules,其中use除了可以使用模块名称,也可以使用模块路径

    开发Plugin思路:

  • Plugin通过钩子机制实现(类似于事件),为了便于插件的扩展,Webpack几乎给每一个环节都埋下了一个钩子,我们在去开发插件的时候就可以通过往这些不同的节点上挂载不同的任务。

  • Webpack要求每一个插件必须是一个函数或者是一个包含apply方法的对象。一般我们都会把这个插件定义为一个类型,然后在这个类型中定义一个apply方法。使用就是通过这个类型构建一个实例去使用。
  • apply方法就是接收一个compiler参数,包含了这次构建的所有配置信息,通过这个对象注册钩子函数
  • 通过compiler.hooks.emit.tap注册钩子函数(emit事件也可以为其他事件),钩子函数第一个参数为插件名称,第二个蚕食compilation为此次打包的上下文,根据compilation.assets就可以拿到此次打包的资源,做一些相应的逻辑处理

二、编程题

1、使用 Webpack 实现 Vue 项目打包任务

具体任务及说明:

  1. 先下载任务的基础代码
  2. 这是一个使用 Vue CLI 创建出来的 Vue 项目基础结构
  3. 有所不同的是这里我移除掉了 vue-cli-service(包含 webpack 等工具的黑盒工具)
  4. 这里的要求就是直接使用 webpack 以及你所了解的周边工具、Loader、Plugin 还原这个项目的打包任务
  5. 尽可能的使用上所有你了解到的功能和特性


项目依赖

  1. "@babel/core": "7.6.3",
  2. "@babel/preset-env": "7.6.3",
  3. "@vue/eslint-config-standard": "5.1.2",
  4. "babel-eslint": "10.1.0",
  5. "babel-loader": "8.0.6",
  6. "clean-webpack-plugin": "3.0.0",
  7. "copy-webpack-plugin": "5.0.4",
  8. "css-loader": "3.2.0",
  9. "eslint": "6.7.2",
  10. "eslint-friendly-formatter": "4.0.1",
  11. "eslint-loader": "4.0.2",
  12. "eslint-plugin-import": "2.20.2",
  13. "eslint-plugin-promise": "4.2.1",
  14. "eslint-plugin-standard": "4.0.0",
  15. "eslint-plugin-vue": "6.2.2",
  16. "file-loader": "4.2.0",
  17. "html-webpack-plugin": "3.2.0",
  18. "less": "3.0.4",
  19. "less-loader": "5.0.0",
  20. "style-loader": "1.0.0",
  21. "url-loader": "2.2.0",
  22. "vue-loader": "15.2.1",
  23. "vue-style-loader": "4.1.0",
  24. "vue-template-compiler": "2.6.12",
  25. "webpack": "4.40.2",
  26. "webpack-cli": "3.3.9",
  27. "webpack-dev-server": "3.9.0",
  28. "webpack-merge": "4.2.2"

全局依赖

  1. 安装yarn
  2. 引入webpack yarn add webpack@4.40.2 webpack-cli@3.3.9 --dev

    common.js module:

  3. 将vue组件编译成普通的JavaScript模块,’vue-template-compiler’被vue-loader所引用,注意版本要与vue相同,不然编译时系统会报错If you are using vue-loader@>=10.0, simply update vue-template-compiler.就是两者必须要版本完全相同。

yarn add vue-template-compiler@2.6.11 vue-loader@15.2.1 --dev

  1. 需要Webpack在打包过程中同时处理其他ES6特性的转换

yarn add babel-loader@8.0.6 @babel/core@7.6.3 @babel/preset-env@7.6.3 --dev

  1. 引入资源模块加载处理CSS文件

yarn add css-loader@3.2.0 vue-style-loader@4.1.0 --dev

  1. 处理 .less 文件的资源模块加载

yarn add less@3.0.4 less-loader@5.0.0 style-loader@1.0.0 --dev

  1. 处理 图片资源模块

yarn add file-loader@4.2.0 url-loader@2.2.0 --dev

  1. ESLint 结合 Webpack

yarn add eslint@6.7.2 eslint-loader@4.0.2
eslint-friendly-formatter@4.0.1 --dev 创建.eslintrc 文件并配置

common.js plugins

  1. 通过 Webpack 输出 HTML 文件

yarn add html-webpack-plugin@3.2.0 --dev

dev.js:

  1. 希望在公共配置原有基础上添加插件而不是覆盖

yarn add webpack-merge@4.2.2 --dev

  1. 提供一个开发服务器

yarn add webpack-dev-server@3.9.0 --dev
在package.json 为 serve 定义 ‘webpack-dev-server —config webpack.dev.js’

prod.js:

  1. 打包之前自动清除目录

yarn add clean-webpack-plugin@3.0.0 --dev

  1. 拷贝静态文件至输出目录

yarn add copy-webpack-plugin@5.0.4 --dev

  1. 在package.json 为 build 定义’webpack —config webpack.prod.js’

    .eslintrc.js:

  2. 配置第三方插件

yarn add eslint-plugin-vue@6.2.2 --dev

  1. eslint-config-standard依赖:eslint-plugin-import eslint-plugin-standard eslint-plugin-promise

yarn add eslint-plugin-import@2.20.2
eslint-plugin-promise@4.2.1
eslint-plugin-standard@4.0.0 --dev

  1. 共享配置

yarn add @vue/eslint-config-standard@5.1.2 --dev

  1. 设置解析器

yarn add babel-eslint@10.1.0 --dev 默认使用Espree

  1. 在package.json 为 lint 定义’eslint —ext .js,.vue src’,为 lintfix 定义 ‘eslint —fix —ext .js,.vue src’

    .eslintig

  2. 在项目根目录创建一个 .eslintignore 文件告诉 ESLint 去忽略特定的文件和目录

  3. .eslintignore 文件是一个纯文本文件,其中的每一行都是一个 glob 模式表明哪些路径应该忽略检测
  4. eslint总是忽略 /node_modules/ 和 /bower_components/ 中的文件
    1. // package.json
    2. // 运行、打包、按要求格式
    3. "scripts": {
    4. "serve": "webpack-dev-server --config webpack.dev.js",
    5. "build": "webpack --config webpack.prod.js",
    6. "lint": "eslint --ext .js,.vue src"
    7. },
    ```javascript // webpack.common.js 公共环境配置

const webpack = require(‘webpack’);

// 默认导出一个插件的类型,不需要结构内部成员 // 配合vue-loader使用,用于编译转换.vue文件 const VueLoaderPlugin = require(‘vue-loader/lib/plugin’); // 用于生成index.html const HtmlWebpackPlugin = require(‘html-webpack-plugin’);

module.exports = { // 输入文件入口 entry: ‘./src/main.js’, // 输出 output: { // 编译文件名 filename: ‘bundle.js’, // 输出目录(绝对路径,通过path转换) // path: path.join(dirname, ‘output’) }, module: { rules: [ { // 匹配打包过程中所有的.vue文件 test: /.vue$/, // 指定匹配到的文件使用到的loader use: [ // 当我们配置多个loader时,执行顺序由下至上 // 将vue组件编译成普通的javascript文件 ‘vue-loader’ ], }, { // 匹配打包过程中所有的.js文件 test: /.js$/, use: { // babel只是转换js代码的平台 loader: ‘babel-loader’, // 需要基于不同插件转换代码中的具体特性 options: { // 使用preset-env插件集合 presets: [‘@babel/preset-env’] }, }, }, { // 匹配打包过程中所有的.css文件 test: /.css$/, use: [ ‘vue-style-loader’, ‘css-loader’ ] }, { // 匹配打包过程中所有的.less文件,配置less-loader,应用到.less文件转换成css代码 test: /.less$/, use: [ ‘style-loader’, ‘css-loader’, // 编译less to css ‘less-loader’ ] }, { // 匹配打包过程中的图片格式 test: /.(png|jpg|gif)$/, use: { // loader: ‘file-loader’, loader: ‘url-loader’, options: { // 小于10kb的文件转化为Data URLs 嵌入代码中 // 超出10kb的文件单独提取存放 limit: 10 * 1024, // 10 KB name: ‘img/[name].[ext]’ } } }, { // 配置 eslint-loader 检查代码规范,应用到 .js 和 .vue 文件 test: /.(js|vue)$/, use: { loader: ‘eslint-loader’, options: { // 默认的错误提示方式 formatter: require(‘eslint-friendly-formatter’) } }, // 编译前检查 enforce: ‘pre’, // 不检查的文件 exclude: /node_modules/, // 要检查的目录 include: [dirname + ‘/src’], }, ], }, plugins: [ new webpack.DefinePlugin({ //其值要求的是一个代码片段 BASE_URL: JSON.stringify(‘’) }), new VueLoaderPlugin(), new HtmlWebpackPlugin({ // 设置模板html标题 title: ‘peiyp vue project’, // 设置使用模板的地址 template: ‘./public/index.html’ }) ], }

  1. ```javascript
  2. // webpack.dev.js 开发环境配置
  3. const webpack = require('webpack')
  4. // 使用merge方法合并配置
  5. const merge = require('webpack-merge')
  6. // 导入公共配置
  7. const common = require('./webpack.common')
  8. module.exports = merge(common, {
  9. // 开发依赖
  10. mode: 'development',
  11. devtool: 'cheap-eval-moudle-source-map',
  12. devServer: {
  13. host: 'localhost',
  14. port: '8090',
  15. // 启动服务时,自动打开浏览器
  16. open: true,
  17. // 开启热更新功能
  18. hot: true,
  19. contentBase: 'public'
  20. },
  21. plugins: [
  22. new webpack.HotModuleReplacementPlugin()
  23. ],
  24. })
  1. // webpack.prod.js 生产环境配置
  2. // 导入公共配置
  3. const common = require('./webpack.common')
  4. // 使用merge方法合并配置
  5. const merge = require('webpack-merge')
  6. // 打包之前清除dist目录
  7. const { CleanWebpack, CleanWebpackPlugin } = require('clean-webpack-plugin')
  8. // 拷贝静态文件到输出目录
  9. const CopyWebpackPlugin = require('copy-webpack-plugin')
  10. module.exports = merge(common, {
  11. mode: 'none',
  12. output: {
  13. // 文件级别的,不同文件就有不同的hash值,现指定8位
  14. filename: 'js/bundle-[contenthash:8].js'
  15. },
  16. plugins: [
  17. new CleanWebpackPlugin(),
  18. // 通配符或‘目录’
  19. new CopyWebpackPlugin(['public'])
  20. ]
  21. })
  1. module.exports = {
  2. // 默认情况下,ESLint 会在所有父级目录里寻找配置文件,一直到根目录。
  3. // 如果发现配置文件中有 “root”: true,它就会停止在父级目录中寻找
  4. "root": true,
  5. // 标记当前代码最终的运行环境
  6. "env": {
  7. // Node.js 全局变量和作用域
  8. "node": true
  9. },
  10. // 记录共享配置
  11. "extends": [
  12. // 插件名称可以省略 eslint-plugin- 前缀
  13. "plugin:vue/essential", // plugin:插件名称/配置名字
  14. // 一个配置文件可以从基础配置中继承已启用的规则。
  15. // 如下,如果值为字符串数组则每个配置继承它前面的配置。
  16. // 值为 "eslint:recommended" 的 extends 属性启用了eslint默认的规则
  17. "eslint:recommended",
  18. "@vue/standard"
  19. ],
  20. "parserOptions": {
  21. // 设置解析器选项能帮助 ESLint 确定什么是解析错误,所有语言选项默认都是 false
  22. // 解析器,默认使用Espree
  23. "parser": "babel-eslint"
  24. },
  25. "rules": { // 配置eslint中每一个校验规则的开启/关闭
  26. // ESLint 附带有大量的规则。你可以在rules选项中设置,
  27. // 设置的规则将覆盖上面继承的默认规则
  28. // 要改变一个规则设置,你必须将规则 ID 设置为下列值之一:
  29. // "off" 或 0 - 关闭规则
  30. // "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  31. // "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
  32. indent: ["error", 4] // 强制使用一致的缩进
  33. // eqeqeq: [2, 'always'], // 要求使用 === 和 !==
  34. // semi: [2, 'never'], // 要求或禁止使用分号代替 ASI
  35. // quotes: [2, 'single'], // 强制使用一致的反勾号、双引号或单引号
  36. }
  37. }
  1. // .eslintgnore
  2. /build/
  3. /config/
  4. /dist/
  5. /static/
  6. /*.js

image.png
image.png

作业要求

本次作业中的编程题要求大家完成相应代码后(二选一)
1. 简单录制一个小视频介绍一下实现思路,并演示一下相关功能。
2. 提交一个项目说明文档,要求思路流程清晰。
最终将录制的视频或说明文档和代码统一提交至作业仓库。