一. Webpack

1. npm包

  • webpack:核心包
  • webpack-cli:webpack命令行工具包
  • webpack-dev-server:开发时快速启动的服务

    2. 占位符

  • [name]:模块名称

  • [id]:模块标识符
  • [hash]:模块标识符的hash
  • [chunkhash]:chunk内容的hash
  • [query]:模块的query,文件名?后面的内容

可以指定长度如[hash:16]

3. entry

文件入口

4. output

文件输出
output.clean:替代clean-webpack-plugin,在打包前清除dist文件夹

5. module

webpack模块,处理项目中不同类型的模块

  • rule.test:匹配文件类型
  • rule.loader:设置单个解析器
  • rule.use:设置多个解析器,并可以添加options配置
  • rule.exclude:排除的文件夹;如babel-loader排除/node_modules/
  • rule.generator.filename:指定资源的输出路径和文件名

    1. module: {
    2. rules: [
    3. {
    4. test: /\.js$/,
    5. exclude: /node_modules/,
    6. loader: 'babel-loader'
    7. },
    8. {
    9. test: /\.vue$/,
    10. loader: 'vue-loader'
    11. },
    12. {
    13. test: /\.(png|jpe?g|gif|svg)$/,
    14. type: 'asset/resource',
    15. parser: {
    16. dataUrlCondition: {
    17. maxSize: 8 * 1024
    18. }
    19. },
    20. generator: {
    21. filename: 'assets/img/[contenthash:6][ext]'
    22. }
    23. }
    24. ]
    25. },
  • rules.type:设置类型用于匹配模块;有如下值处理不同类型的文件

    • asset/resource:发送一个单独的文件并导出URI,之前通过file-loader实现。可以处理png、jpeg,gif等资源 ```javascript module: { rules: [ { test: /.png/, type: ‘asset/resource’ } ] }

// 所有的png文件都被发送到输出目录,其路径被注入到bundle中 import mainImage from ‘./images/main.png’; img.src = mainImage; // ‘/dist/151cfcfa1bd74779aadb.png’

  1. - `asset/inline`:导出一个data uri,之前通过`url-loader实现`,可以处理svg
  2. ```javascript
  3. module: {
  4. rules: [
  5. {
  6. test: /\.svg/,
  7. type: 'asset/inline'
  8. }
  9. ]
  10. }
  11. // 所有的svg文件都将作为data uri注入到bundle中
  12. import metroMap from './images/metro.svg';
  13. block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
  • asset:webpack按照默认规则自动在resource、inline之间选择;默认小于8kb选择视为inline模块;否则视为resource模块。这里使用rule.parser.dataUrlCondition配置多大的模块使用base64内置
    1. module: {
    2. rules: [
    3. {
    4. test: /\.(png|jpe?g|gif|svg)$/,
    5. type: 'asset',
    6. parser: {
    7. dataUrlCondition: {
    8. maxSize: 8 * 1024
    9. }
    10. }
    11. }
    12. ]
    13. },

    6. resolve

    配置模块的路径如何解析
  • resolve.alias:路径的别名,优先级高于其他规则;常用@表示src
    1. resolve: {
    2. alias: {
    3. '@': path.resolve(__dirname, 'src')
    4. }
    5. },

    7.devtool

    配置如何生成source map
    development环境推荐eval-cheap-module-source-map
    production环境可以不添加这个选项不生成source map

    各种loader

    1) vue-loader
    webpack插件VueLoaderPlugin:将定义过的其他规则复制到vue中;如/\.js$/复制到vue文件的<script>标签
    2) css-loader和style-loader
    css-loader用于识别并加载css,style-loader用于将css转换为HTML中的style节点;
    css-loader importLoaders:有@import语法的时候,importLoaders决定@import模块在使用css-loaders前使用多少个其他的loaders处理
    1. // importLoaders为2;说明@import模块在使用css-loader之前使用postcss-loader和sass-loader处理
    2. module: {
    3. rules: [
    4. {
    5. test: /\.sass$/,
    6. use: [
    7. 'style-loader',
    8. {
    9. loader: 'css-loader',
    10. options: {
    11. importLoaders: 2
    12. }
    13. },
    14. 'sass-loader',
    15. 'postcss-loader'
    16. ]
    17. }
    18. ]
    19. }
    3) sass-loader
    支持解析sass,scss。
    避免在@import使用webpack中的alias(如@代表src),会识别失败
    参考:webpack5 的使用(三):加载 css
    4) postcss-loader
    将css解析成AST,通过其他插件做各种修改最终生成新的css
    这是使用postcss添加浏览器前缀的方式
  1. 首先安装3个npm包

    1. npm install -D postcss postcss-loader autoprefixer
  2. 在处理css,scss,sass的规则中添加postcss-loader

    1. module: {
    2. rules: [
    3. {
    4. test: /\.sass$/,
    5. use: [
    6. 'style-loader',
    7. {
    8. loader: 'css-loader',
    9. options: {
    10. importLoaders: 2
    11. }
    12. },
    13. 'sass-loader',
    14. 'postcss-loader'
    15. ]
    16. }
    17. ]
    18. }
  3. 添加postcss.config.js和.browserslistrc

    1. // postcss.config.js
    2. module.exports = {
    3. plugins: [
    4. require('autoprefixer'),
    5. // 这是将px转化为vw的插件,不需要请忽略
    6. {
    7. 'postcss-px-to-viewport': {
    8. viewportWidth: 375
    9. }
    10. }
    11. ]
    12. };
    13. // .browserslistrc
    14. > 0.2%
    15. last 1 Chrome versions
    16. not dead

    参考:Webpack5 从零配置一个基础的 Vue 项目

    各种plugin

    1) html-webpack-plugin

    当使用webpack打包的时候,创建一个HTML文件,并把打包后的静态文件自动插入到html文件中;注意需要设置favicon的路径,这样webpack运行的时候才能正确将favicon.ico放在dist目录下。
    参考:webpack5 + vue3 从零配置项目

    2) clean-webpack-plugin

    在打包前清除output配置的文件夹,webpack5中可以用output.clean: true替代

    3) webpack-bundle-analyzer

    分析打包后模块体积的工具,可用于打包优化分析

    4) terser-webpack-plugin

    js压缩工具,webapck5在生产环境自动开启

    5) mini-css-extract-plugin

    提取css到单独的文件夹;建议从bundle提取css;需要同时设定module和plugin

    1. module: {
    2. rules: [
    3. {
    4. test: /\.css$/,
    5. use: [
    6. // 生产环境用MiniCssExtractPlugin.loader代理style-loader
    7. MiniCssExtractPlugin.loader,
    8. {
    9. loader: 'css-loader',
    10. options: {
    11. importLoaders: 1
    12. }
    13. },
    14. 'postcss-loader'
    15. ]
    16. },
    17. {
    18. test: /\.s[ca]ss/,
    19. use: [
    20. MiniCssExtractPlugin.loader,
    21. {
    22. loader: 'css-loader',
    23. options: {
    24. importLoaders: 2
    25. }
    26. },
    27. 'sass-loader',
    28. 'postcss-loader'
    29. ]
    30. }
    31. ]
    32. },
    33. plugins: [
    34. new MiniCssExtractPlugin({
    35. filename: 'css/[name].css'
    36. })
    37. ]

    其他工具

    1) webpack-merge

    合并各个webpack配置 ```javascript const common = require(‘./webpack.common’); const { merge } = require(‘webpack-merge’);

module.exports = merge(common, { mode: ‘development’ });

  1. <a name="Nxke3"></a>
  2. ### 二. babel
  3. <a name="cIimG"></a>
  4. #### 1) babel提供语法转换与polyfill功能
  5. 1. `@babel/core`:核心包
  6. 1. `babel-loader`:webpack中处理js的loader包
  7. 1. `@babel/preset-env`:babel预设环境,它会根据`browserlist`进行语法转换
  8. 1. `@vue/babel-preset-app`:vue的预设环境;
  9. 1. `@babel/plugin-transfrom-runtime`:编译时转译代码;所以用于开发环境
  10. 1. `@babel/runtime`:运行时转译代码,只能处理语法syntax;用于生产环境
  11. 1. `@babel/runtime-corejs3`:运行转译代码,可以处理语法,引入polyfill处理api;用于生产环境
  12. <a name="smoGJ"></a>
  13. #### 2) 不同的代码转换配置:
  14. <a name="eI3Ll"></a>
  15. ##### 1. 不做任何配置:原样输出
  16. <a name="p4Kc5"></a>
  17. ##### 2. `@Babel/preset-env`
  18. 1. 只转换syntax(class,typeof,箭头函数等),不转换api(map,include)
  19. 1. syntax的转换策略由browserslist而定
  20. ```javascript
  21. // babel.config.js
  22. module.exports = {
  23. presets: [
  24. [
  25. '@babel/preset-env'
  26. ]
  27. ]
  28. }
  29. // .browserslistrc
  30. >0.25%

3. @Babel/preset-env + core-js@3
  1. 转换syntax和api
  2. syntax的转换策略由browserslist而定
  3. polyfill从core-js@3中引入
  4. useBuiltIns:是加载polyfill的策略
    1. usage,无需手动import '@babel/polyfill'根据browserslist策略 + 使用情况按需加上polyfill
    2. false,不使用polyfill,如果手动import '@babel/polyfill'会无视策略全部加载polyfill
    3. entry:需要手动import '@babel/polyfill',根据浏览器策略加载polyfill,导致用不到的polyfill也会被加进去 ```javascript // babel.config.js module.exports = { presets: [ [ ‘@babel/preset-env’, { corejs: 3, useBuiltIns: ‘usage’ } ] ] }
  1. <a name="qcWLe"></a>
  2. ##### 4. 组合使用`@babel/preset-env`, `core-js@3`, `@babel/plugin-transform-runtime`, `@babel/runtime-corejs3`
  3. 4. core-js设置转译api,runtime设置false不转译api
  4. 4. runtime提取了helper代码,减少了代码重复
  5. ```json
  6. {
  7. "plugins": [
  8. [
  9. "@babel/plugin-transform-runtime", // 编译时开发环境转译
  10. {
  11. "corejs": false, // runtime不转译api,由下面的corejs转译
  12. "helpers": true, // 提取helpers代码
  13. "regenerator": false
  14. }
  15. ]
  16. ],
  17. "presets": [
  18. [
  19. "@babel/preset-env", // babel转换syntax
  20. {
  21. "corejs": 3, // corejs转译api,polyfill
  22. "useBuiltIns": "usage" // 按需引入
  23. }
  24. ]
  25. ]
  26. }

参考:Babel polyfill 常见配置对比

三. eslint + prettier +vue

1. eslint

.eslintrc.js配置选项的说明

  1. root:停止父目录寻找配置文件
  2. parserOptions.sourceType:默认scirpt,如果是ES模块设为module
  3. parserOptions.parser:默认Espree解析器;如果有babel可以使用@babel/eslint-parser解析器
  4. env:预定义的全局变量;可用的环境可以有browser,node,commonjs等
  5. globals:添加自定义的全局变量名称
  6. extends:扩展;值的说明

    1. eslint:recommended:eslint推荐的配置
    2. plugin:vue/vue3-recommended:eslint-plugin-vue推荐的配置
    3. prettier:使用prettier的配置

      2. prettier

      配置参数:
  7. printWidth:允许的最长宽度

  8. semi:是否加分号
  9. singleQuote:是否使用单引号
  10. trailingComma:是否在末尾追加逗号,可以设置为'none'
  11. endOfLine:windows和linux换行报错问题;可以设置为'auto'

    3. eslint+prettier+vue3+vscode配置最佳实践

  12. 安装eslint,prettier,eslint-plugin-vue,eslint-config-prettier,@babel/eslint-parser

    1. npm i -D eslint prettier eslint-plugin-vue eslint-config-prettier @babel/eslint-parser

    各个插件的作用如下:

eslint-plugin-vue 允许检查vue文件的<template>,<script>和js文件的vue代码
eslint-config-prettier 关闭eslint中与prettier冲突的规则
eslint-plugin-prettier 以eslint的方式运行prettier,并报错
@babel/eslint-parser 允许检查所有合法的babel代码
  1. 添加.eslintrc.js文件,设置eslint ```javascript module.exports = { root: true, // 不从父文件夹检查eslint配置文件 parserOptions: { sourceType: ‘module’, // 默认script,如果是ES模块则是module parser: ‘@babel/eslint-parser’ // 允许所有合法的babel代码 }, env: { // 预设了哪些环境的全局变量 browser: true, node: true, es2021: true }, plugins: [‘prettier’], // 添加eslint-plugin-prettier插件 extends: [ ‘eslint:recommended’, // eslint推荐的配置 ‘plugin:vue/vue3-recommended’, // eslint-plugin-vue包内的vue3配置;vue2使用vue/essential ‘prettier’ // prettier配置 ], globals: { // 自定义全局变量 defineProps: true }, rules: { // 自定义规则 ‘prettier/prettier’: ‘error’ // prettier的规则报错 } };
  1. 3. 添加.prettierrc.js文件,设置prettier
  2. ```javascript
  3. module.exports = {
  4. printWidth: 100, // 一行文字长度的限制
  5. semi: true, // 是否添加分号
  6. singleQuote: true, // 是否使用单引号
  7. trailingComma: 'none', // 是否追加逗号
  8. endOfLine: 'auto' // 处理windows和linux的换行不一致
  9. }
  1. 此时已经可以lint了,在package.json的scripts添加lint,fix,prettier,format等脚本

    1. {
    2. "scripts": {
    3. "lint": "eslint --ext .js,.vue src",
    4. "fix": "eslint --ext .js,.vue src --fix",
    5. "prettier:fix": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,less,md,vue}",
    6. "format": "npm run prettier:fix && npm run fix"
    7. }
    8. }
  2. 要在vscode中保存时自动lint;在.vscode文件夹中添加settings.json ```json { “editor.formatOnSave”: true, // 保存时format “editor.codeActionsOnSave”: { “source.fixAll.eslint”: true // 使用eslint来format }, “[vue]”: { “editor.defaultFormatter”: “esbenp.prettier-vscode” }, “[javascript]”: { “editor.defaultFormatter”: “esbenp.prettier-vscode” // 对vue和js使用prettier插件 } }

  1. 参考:[ESLint and Prettier with Vite and Vue.js 3](https://vueschool.io/articles/vuejs-tutorials/eslint-and-prettier-with-vite-and-vue-js-3/)<br /> [非vue/cli创建的vue2项目引入eslint和prettier最佳实践总结](https://juejin.cn/post/7073492841715990559)
  2. <a name="jGHne"></a>
  3. ### 四. Typescript + Koa搭建
  4. 1. 安装typescript
  5. ```shell
  6. npm i -D typescript
  1. 安装ts-node-dev,支持在开发中启动并更新node ```shell // shell npm i -D ts-node-dev

// package.json ts-node-dev app.ts

  1. 3. 开始安装typescriptkoa依赖,koanode的类型支持
  2. ```shell
  3. npm i koa @types/koa typescript @types/node
  1. 初始化TyepScript配置文件tsconfig.json

    1. tsc --init
  2. 在tsconfig.json中配置编译输出的目录

    1. "outdir": "./dist"
  3. 为项目添加eslint,ts-eslint-parser和ts-eslint-plugin

    1. npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

    在默认生成的.eslintrc.js中修改一下,这里eslitn使用的最基本的lint配置 ```javascript module.exports = { // 使用环境node env: {

    1. node: true,
    2. es2021: true

    }, // 定义一个自己的ts解析器 parser: ‘@typescript-eslint/parser’, parserOptions: {

    1. ecmaVersion: 'latest',
    2. sourceType: 'module',
    3. // 告诉eslint typescript配置文件位置
    4. project: ['./tsconfig.json']

    },

    plugins: [

    1. '@typescript-eslint'

    ],

    extends: [

    1. 'eslint:recommended',
    2. 'plugin:@typescript-eslint/recommended',
    3. 'plugin:@typescript-eslint/recommended-requiring-type-checking'

    ],

    rules: { } }

  1. 参考:[ESLint x TypeScript](https://github.yanhaixiang.com/linter-guide/practice/eslint_typescript.html#%E5%86%8D%E6%97%A0-tslint)
  2. 7. 安装prettier,并集成到eslint
  3. ```shell
  4. npm i -D prettier eslint-config-prettier
  1. // .prettierrc.js
  2. module.exports = {
  3. printWidth: 100,
  4. semi: true,
  5. singleQuote: true,
  6. trailingComma: 'none',
  7. endOfLine: 'auto'
  8. }
  9. // eslintrc.js
  10. module.exports = {
  11. root: true,
  12. env: {
  13. node: true,
  14. es2021: true
  15. },
  16. parser: '@typescript-eslint/parser',
  17. parserOptions: {
  18. ecmaVersion: 'latest',
  19. sourceType: 'module',
  20. project: ['./tsconfig.json']
  21. },
  22. plugins: [
  23. '@typescript-eslint',
  24. // 这是eslint-plugin-prettier, 让prettier以eslint的方式运行
  25. 'prettier'
  26. ],
  27. extends: [
  28. 'eslint:recommended',
  29. 'plugin:@typescript-eslint/recommended',
  30. 'plugin:@typescript-eslint/recommended-requiring-type-checking',
  31. // 使用eslint的拓展,'prettier/@typescript-eslint'的拓展已经被集成到这里
  32. 'prettier'
  33. ],
  34. rules: {
  35. // 配合plugin使用
  36. 'prettier/prettier': 'error'
  37. }
  38. }
  1. vscode保存时自动lint,在setting.json中
    1. {
    2. "editor.formatOnSave": true,
    3. "editor.codeActionsOnSave": {
    4. "source.fixAll.eslint": true
    5. },
    6. "[typescript]": {
    7. "editor.defaultFormatter": "esbenp.prettier-vscode",
    8. "editor.tabSize": 2
    9. }
    10. }

    五. mock数据的n种方法

    1. 同源数据的mock

    1) 侵入式的mock

    不太推荐,把mock数据import进项目中,要判断运行环境。

    2) 非侵入式的mock

    使用webpack的devServer.setupMiddlewares,它为开发环境提供了自定义的中间件能力;接受一个函数,有middlewares和devServer两个参数;middlewares是个数组,请求按先后顺序在middlewares中处理。中间件是express风格的,因为就是基于express的。+ mockjs封装一下就可以mock啦
    1. {
    2. devServer: {
    3. setupMiddlewares: function(middlewares, devServer) {
    4. devServer.app.get('hello', (req, res) => {
    5. res.send('hi world')
    6. })
    7. middlewares.push({
    8. name: 'first-middleware',
    9. path: '/hi', // 请求的路径
    10. middleware: (req, res) => {
    11. res.send('hello world')
    12. }
    13. })
    14. return middlewares
    15. }
    16. }
    17. }
    参考:webpack devServer setupMiddlewares

    2. 跨域数据mock

    跨域数据的mock没有什么找到什么好的资料。干脆直接使用koa+mockjs直接起一个node服务。 ```javascript const Koa = require(‘koa’); const Router = require(‘koa-router’); const Mock = require(‘mockjs’);

const app = new Koa(); const router = new Router(); app.use(async (ctx, next) => { ctx.set(‘Access-Control-Allow-Origin’, ctx.headers.origin); ctx.set(‘Access-Control-Allow-Methods’, ‘GET, POST, OPTIONS, PUT, DELETE’); ctx.set(‘Access-Control-Allow-Headers’, ‘*’); ctx.set(‘Access-Control-Allow-Credentials’, true); next(); });

router.get(‘/hi’, (ctx) => { const data = Mock.mock({ description: ‘测试的数据’ }); ctx.body = data; });

app.use(router.routes()).use(router.allowedMethods()); const port = 9091; app.listen(port, () => { console.log(‘跨域服务器已启动…’); console.log(端口${port}); });

  1. <a name="lmPlc"></a>
  2. ### 六. 如何支持打包后动态修改配置
  3. 有时候打包后的包需要部署在不同的服务器中,需要支持修改配置文件,以便修改请求地址
  4. <a name="z0XH2"></a>
  5. #### 1. 在index.html模板中添加script
  6. 在index.html模板中添加script,注意最好插在头部,防止后面的脚本获取不到。将变量暴露挂载到window下
  7. ```html
  8. <script src="./config.js"></script>
  1. // config.js
  2. window.config = {
  3. BASE_URL: 'http://localhost:8080'
  4. }
  5. // 项目中直接获取变量
  6. window.config.BASE_URL

可以把config.js放在项目static目录下,打包时在webpack中使用copy-webpack-plugin将config文件原封不动的复制到dist根目录下。其实public/favicon.ico文件同样可以复制过去。

2. axios请求拦截

使用请求的方式获取数据config.json; 这种方法较为繁琐,不会污染全局变量

  1. 首先创建一个axios实例,在请求拦截器中判断配置是否读取到。如果没有读取到则先请求获取数据。请求拦截器的第一个回调函数是支持异步的
  2. 如果数据没有缓存下来则会去请求获取config
  3. 严重问题,因为是异步的,所以在返回数据前所有的请求都会进入拦截请求一遍;

解决方案:添加一个tag位,保存const tag = axios.get('config.json'),如果tag已经存在就不再请求

  1. /** 请求拦截 */
  2. service.interceptors.request.use(
  3. async (config) => {
  4. /** 请求配置文件,将BASE_URL存放在localStorage中 */
  5. if (!localStorage.get('BASE_URL')) {
  6. const response = await axios.get('config.json');
  7. const global = response.data;
  8. config.baseURL = global?.BASE_URL;
  9. localStorage.setItem('BASE_URL', config.baseURL)
  10. }
  11. return config;
  12. },
  13. (error) => {
  14. return Promise.reject(error);
  15. }
  16. );

参考:【vue】实现项目打包完成后修改接口地址

七. 如何修改IOS15 Safari顶部状态栏颜色

IOS15的Safari浏览器会根据html头部的meta信息修改状态栏颜色,如果没有会自适应使用background。
在vue中路由使用hash模式,更改background的时候并不会让Safari状态栏主动更新。需要手动设置更新

  1. vue的html模板中添加<meta name="theme-color" content="#fff" />
  2. 使用vue不同的页面const el = document.querySelector('meta[name="theme-color"]')获取meta元素
  3. 设置content属性el.setAttribute('content', 'red')

    八. git

    1. git回退版本

    git reset --hard commit_id回退到某个版本,这个版本之后的提交不保留
    git revert -n commit_id删除某次提交,这个提交之后的提交保留

    2. tag和branch区别

    tag是一个里程碑,静态的。可以为发布的里程碑打tag
    branch是动态的,代码需要向前走

    九. 工程化项目CDN加速

    配合webpack抽离出某些包使用cdn加速

  4. 在模板index.html中添加CDN链接,这里我们添加一个axios

    1. <body>
    2. <div id="app"></div>
    3. <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.1/axios.min.js"></script>
    4. </body>
  5. 在webpack中添加externals,代表不需要将这个模块打包到最终的包中。

    1. externals: {
    2. axios: 'axios'
    3. },
  6. 代码中的import不需要删除,因为开发环境会使用到

    十. TSX+ Vue

    自从升级到Vue3,Vue和Webpack就成了路人,Vue官方大力推广自家的vite。诶,vite是要学。
    坑点1:@vue/eslint-config-prettier不认prettierrc,所以只能在eslintrc中设置

    1. module.exports = {
    2. root: true,
    3. extends: [
    4. 'plugin:vue/vue3-essential',
    5. 'eslint:recommended',
    6. '@vue/eslint-config-typescript/recommended',
    7. '@vue/eslint-config-prettier'
    8. ],
    9. env: {
    10. 'vue/setup-compiler-macros': true
    11. },
    12. rules: {
    13. 'prettier/prettier': [
    14. 'warn',
    15. {
    16. printWidth: 100,
    17. semi: true,
    18. singleQuote: true,
    19. trailingComma: 'none',
    20. endOfLine: 'auto'
    21. }
    22. ]
    23. }
    24. };