模块化开发
模块化演变过程
- 文件划分方式
- 污染全局作用域
- 命名冲突
- 无法管理模块依赖关系
- 命名空间方式
- 每个模块只暴露一个全局对象,所有模块成员都挂载到这个对象中
- 模块成员可以被修改
- IIFE(立即执行函数表达式)
- 为模块提供私有空间
- 模块私有成员无法访问
模块化规范
- CommonJS规范(用于 node)
- 一个文件就是一个模块
- 每个模块都有独立的作用域
- 通过 module.exports 导出成员
- 通过 require 导入成员
- 以同步模式加载模块
- AMD + Require.js
- define 定义模块;require 载入模块
- 使用起来相对复杂
- 模块 Js 文件请求频繁
- Sea.js + CMD
- CMD 规范类似于 Commonjs规范
- ES Modules
- ES6提出的用于浏览器的标准模块化规范
ES Modules
-
基本特性
- 自动使用严格模式,忽略"use strict"
- 每个 ESM 模块都是单独的私有作用域
- ESM 通过 CORS 去请求外部 JS 模块
- ESM 的 script 标签会延迟执行脚本
-
ES Modules 导出
// import { name } 搭配使用 export var name = "123" // import { fooName, helloFunction } 搭配使用 export { name as fooName, helloFunction } // import { default as 任意除 default(或其余关键字) 之外变量名 } 搭配使用 export { name as default } // import 任意除 default(或其余关键字) 之外变量名 搭配使用 export default name
-
导入导出的注意事项
// 这里不是对象字面量,而是语法规则 export { name, age } // 而 export default 导出的是字面量对象 export default { name, age } // 这里不是解构字面量对象,与 export { name, age }搭配使用 import { name, age }
- 导入成员并不是复制副本,而是直接导入模块成员的引用地址,即 import 的变量与 export 的变量在内存中是同一块空间。一旦在 export 时成员修改了,import 得到的变量也会同步修改
- import 模块成员变量是只读的,注意如果导入对象,对象的属性读写不受影响
-
导入用法
// 只能引入完成路径,不能忽略文件类型,不能忽略"/" // 只加载模块 import "./modules.js" // 提取全部成员 import * as all from "./modules.js" // 动态引入模块 import("./modules.js").then(function(module) { console.log(module) }) // 提取默认成员和其他成员 import { name, age, default as title} from "./modules.js" import title, { name, age } from "./modules.js"
-
导出导入成员
// 直接使用 export 导出导入的成员,就不用先 import 再 export export { defalut as Button } from "./button.js" export { Avatar } from "./avatar.js"
-
浏览器环境的支持 polyfill
// 只适合开发环境 <script nomodule src="https://unpkg.com/promise-polyfill@8.1.3/dist/polyfill.min.js"></script> <script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script> <script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
-
ES Modules in node.js
- 若想要 node.js 支持 ES Modules,文件后缀改成 .mjs
- 命令行执行 node --experimental-modules **.mjs
-
ES Modules in node.js 与 CommonJS 交互
- ES Modules 中可以导入 CommonJS 模块
- CommonJS 中不能导入 ES Modules 模块
- CommonJS 始终只能导出一个默认成员
- 注意 import 不是解构导出对象
-
ES Modules in node.js 与 CommonJS 差异
- require、module、exports、__dirname、__filename 为 全局成员,但是 ES Modules 没有这些成员,但可以有其余属性代替
- require、module、exports 通过 ES Modules 的 import、export 代替
// __filename 和 __dirname 通过 import.meta.url 属性可以获取 import { fileURLToPath } from "url" import { dirname } from "path" const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename)
-
ES Modules in node.js - 新版本进一步支持
- Node v12 以上版本,在 package.json 添加 { type: “module” },将模块系统默认修改为 ES Modules,此时不需要 mjs 文件拓展名
- 若在 { type: “module” } 继续使用 CommonJS,文件拓展名改为 .cjs
-
ES Modules in node.js - Babel 兼容方案
- 借助 Babel 在低版本 node 环境下使用 ES Modules
- 安装 @babel/node @babel/core @babel/preset-env @babel/plugin-transform-modules-commonjs
- 新建 .babelrc 文件,两种配置如下
// @babel/preset-env 插件集合 { "presets": "@babel/preset-env" } // 或者使用单独的插件,效率更高,需要什么插件再引入什么插件 { "plugins": [ "@babel/plugin-transform-modules-commonjs" ] }
Webpack 打包
-
模块打包工具由来
- 模块化存在的问题
- ES Modules 存在环境兼容
- 模块文件过多,网络请求频繁
- 所有的前端资源都需要模块化
- 模块打包工具可解决问题
- 新特性代码编译
- 模块化 JS 打包
- 支持不同类型的资源模块
- 模块化存在的问题
-
Webpack 快速上手
yarn init --yes yarn add webpack webapck-cli -d yarn webpack
-
Webpack 配置文件
- 新建 webpack.config.js 文件
const path = require("path") module.exports = { entry: "./src/main.js", output: { filename: "bundle.js", path: path.join(__dirname, "output") } }
-
Webpack 工作模式
在 webpack.config.js 文件下添加字段 mode 配置- production:默认模式,自动启用优化打包结果
- development:自动优化打包速度,添加调试用到的辅助到代码中
- none:运行最原始的打包,不会做任何额外处理
-
Webpack 打包结果运行界面
- 入口函数接收模块参数
- 先判断模块是否被加载过,有则从缓存中读,无则重新加载
- 调用 r 函数给导出模块做标记
-
Webpack 资源模块加载
- loader 是 Webpack 的核心特性
- 借助于 Loder 就可以加载任何类型的资源
-
Webpack 导入资源模块
- 入口文件应该是 JS 文件
- 在 JS 文件中引入 CSS
- 逻辑合理,JS 确实需要这些资源文件
- 确保上线资源不缺失,都是必要的
-
Webpack 文件资源加载
- output 添加属性
publicPath: "dist/"
- 引入 file-loader,module 添加配置
{ test: /.png$/, use: "file-loader" }
- output 添加属性
-
Webpack URL 加载器
- Data URLs 是一种特殊的 url 协议,可以直接用来表示一个文件
- 小文件使用 Data URLs,减少请求次数
- 大文件单独提取存放,提高加载速度
- 使用 url-loader 也需要安装 file-loader
module: { rules: [ { test: /.png$/, use: { loader: "url-loader", options: { limit: 10 * 1024 // 10k } } } ] }
-
Webpack 常用加载器分类
- 编译转换类:编译代码,如 css-loader、style-loader
- 文件操作类:拷贝文件等,如 file-loader
- 代码检查类:检查代码是否通过,不会更改生产代码,如 eslint-loader
-
Webpack 与 ES2015
- Webpack 只是打包工具
- 加载器用来编译转换代码
- 使用 babel-loader、@babel/core、@babel/preset-env 编译 ES6 新特性为 ES5
{ test: /.js$/, use: { loader: "babel-loader", // 转换 js 平台 options: { presets: ["@babel/preset-env"] // 插件集合编译具体新特性 } } },
-
Webpack 加载资源的方式
- 遵循 ES Modules 标准的 import 声明
- 遵循 CommonJS 标准的 require 函数
- 遵循 AMD 标准的 define 函数 和 require 函数
- 样式代码中的 @import 指令和 url 函数
- HTML 代码中图片标签的 src 函数
- 引入 html-loader 处理 html 打包
{ test: /.html$/, use: { loader: "html-loader", options: { attrs: ["img:src", "a:href"] } } }
-
Webpack 核心工作原理
- 根据配置找到入口
- 顺着入口找到依赖文件,形成依赖树
- Webpack 递归依赖树找到每个节点对应资源的文件
- 交给对应的加载器加载文件
- 将加载结果放入到打包结果中
-
Webpack 开发一个 Loader
- Loader 负责资源文件从输入到输出的转换
- 对于同一个资源可以依次使用多个 loader
- loader 具有工作管道特性
// 新建 markdown-loader.js 文件 const marked = require('marked') module.exports = source => { // console.log(source) // return 'console.log("hello ~")' const html = marked(source) // return html // return `module.exports = "${html}"` // return `export default ${JSON.stringify(html)}` // 返回 html 字符串交给下一个 loader 处理 return html }
{ test: /.md$/, use: [ 'html-loader', './markdown-loader' ] }
-
Webpack 插件机制介绍
- Loader 专注实现资源模块加载
- Plugin 解决其他自动化工作
- 清除 dist 目录
- 拷贝静态文件至输出目录
- 压缩输出代码
-
Webpack 自动清除输出目录插件
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
module: .... // 此处省略
plugins: [
new CleanWebpackPlugin()
]
}
- Webpack 自动生成 HTML 插件
// src/index.html 模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack</title>
</head>
<body>
<div class="container">
// 会根据 plugin 配置渲染
<h1><%= htmlWebpackPlugin.options.title %></h1>
</div>
</body>
</html>
// webpack.config.js 文件
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
module: .... // 此处省略
plugins: [
// 用于生成 index.html
// 由于引入 bundle.js 路径变了,此文件配置已不需要 publicPath: "dist/"
new HtmlWebpackPlugin(
title: "Webpack Plugin Sample",
meta: {
viewport: "width=device-width"
},
template: "./src/index.html"
),
// 用于生成 about.html
new HtmlWebpackPlugin(
// 此值默认为 index.html
filename: "about.html",
),
]
}
-
Webpack 插件使用总结
- 去 github 上可以查找到很多插件,插件的用法都是类似的
- 复制 public 里面的所有文件
// webpack.config.js 文件 const CopyWebpackPlugin = require("copy-webpack-plugin") module.exports = { module: .... // 此处省略 plugins: [ new CopyWebpackPlugin([ // 'public/**' 'public' ]) ] }
-
Webpack 开发一个插件
- 通过钩子机制实现,Webpack 在不同时期有不同的钩子,在钩子上挂载任务
- 插件必须是一个函数或者是一个包含 apply 方法的对象,通常是后者
- 接收一个 compiler 参数,通过这个对象注册钩子函数
- 通过 tap 注册一个钩子函数,第一个参数为插件名,第二个参数为挂载到此钩子上的函数,接收 compilation (此次打包上下文)对象参数
- 访问文件资源,逻辑处理固定返回 source 内容 和 size 文件大小
class MyPlugin { apply(compiler) { console.log("MyPlugin启动") compiler.hooks.emit.tap("MyPlugin", compilation => { // compilation 此次打包上下文 console.log(compilation.assets) for (const name in compilation.assets) { console.log(name) console.log(compilation.assets[name].source) if (name.endsWith(".js")) { const contents = compilation.assets[name].source() const withoutComments = contents.replace(/\/\*\*+\*\//g, "") compilation.assets[name] = { source: () => withoutComments, size: () => withoutComments.length } } } }) } }
-
Webpack 开发体验问题
- 以 HTTP Server 运行
- 自动编译 + 自动刷新
- 提供 Source Map 支持
-
Webpack 自动编译 + 自动刷新浏览器
- 执行命令 yarn webpack --watch,Webpack 以监视模式运行,等待文件变化再次工作
- 再开启一个命令行终端,执行命令 browser-sync dist --files “**/*” 运行应用
- 以上操作麻烦,效率低下
-
Webpack Dev Server
- 执行命令 yarn webpack-dev-server --open 实现自动编译 + 自动刷新浏览器
- 运行不生成 dist 文件,存储在内存中,HTTP Server直接读内存,效率更高
- 开发阶段不要使用 CopyWebpackPlugin 这个插件
// webpack.config.js devServer: { contentBase: "./public" // 额外为开发服务器指定查找资源目录 proxy: { "/api": { // http://localhost:8080/api/users -> https://api.github.com/api/users target: "https://api.github.com", // http://localhost:8080/api/users -> https://api.github.com/users pathRewrite: { "^/api": "" }, // 不能使用 localhost:8080 作为请求 GitHub 的主机名 changeOrigin: true, } } }
-
SourceMap
- 映射转换后的代码与源代码的关系
- 解决了源代码与运行代码不一致所产生的问题
- 若想在浏览器控制面板 SourceMap,比如可以在 jquery-3.4.1.min.js 最后一行添加
//# sourceMappingURL=jquery-3.4.1.min.map
-
Webpack devtool 模式
- eval:是否使用 eval 执行模块代码
- cheap:Source Map 是否包含列信息
- module:是否能够得到 Loader 处理之前的源代码
- devtool: “eval” 不会生成 Soource Map,只能定位文件
- devtool: “eval-source-map” 生成 Source Map,定位行列
- devtool: “cheap-eval-source-map” 生成 Soource Map,定位行,但不定位列
- devtool: “cheap-module-eval-source-map” 生成 Soource Map,定位行,能得到 Loader 处理之前的源代码
-
Webapck 选择 Source Map 模式
- 开发环境:cheap-module-eval-source-map
- 生产环境:none(不能定位到源文件,也看不见源代码)/nosources-source-map(能定位到源文件,但看不见源代码)
-
Webpack 自动刷新的问题
- 问题核心:自动刷新导致的页面状态丢失
- 解决思路:页面不刷新的前提下,模块也可以及时更新
-
Webapck HMR 体验
- Hot Module Replacement 模块热更新
- 只将修改的模块实时替换至应用中
-
Webapck 开启 HMR
const webpack = require("webpack")
devServer: {
hot: true
},
plugins: {
new webapck.hotModuleReplacementPlugin()
}
-
Webpack HMR 的疑问
- 为什么样式文件的热更新开箱即用
因为 style-loader 已经自动处理了样式的热更新 - 凭什么样式可以自动处理
因为样式有规律,但是 JS 毫无规律 - 我的项目没有手动处理,JS 照样可以热更新
框架已经处理热更新
- 为什么样式文件的热更新开箱即用
-
Webpack 处理 JS、图片模块热更新
使用 module.hot.accept(使用热更新的文件,处理函数)let hotEditor = editor module.hot.accept('./editor.js', () => { const value = hotEditor.innerHTML document.body.removeChild(hotEditor) hotEditor = createEditor() hotEditor.innerHTML = value document.body.appendChild(hotEditor) }) module.hot.accept('./better.png', () => { img.src = background console.log(1) })
-
Webpack HMR 注意事项
- 处理 HMR 的代码错误会导致自动刷新,可采取 hotOnly: true
- 没开启 HMR 的情况下,HMR API 报错,可采取 if(module.hot) {}
- 代码中多了一些与业务无关的代码
-
Webpack 生产环境优化
模式 mode 为不同的工作环境创建不同的配置 -
Webpack 不同环境下的配置
- 配置文件根据环境不同导出的配置
module.exports = (env, argv) => { const config = { mode: 'development', entry: './src/main.js', output: { filename: 'js/bundle.js' }, devtool: 'cheap-eval-module-source-map', devServer: { hot: true, contentBase: 'public' }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: { loader: 'file-loader', options: { outputPath: 'img', name: '[name].[ext]' } } } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }), new webpack.HotModuleReplacementPlugin() ] } // 当运行命令行 yarn webpack --env production 时则执行如下代码 if (env === 'production') { config.mode = 'production' config.devtool = false config.plugins = [ ...config.plugins, new CleanWebpackPlugin(), new CopyWebpackPlugin(['public']) ] } return config }
-
Webpack 不同环境下的配置文件
- 创建三个文件,webpack.common.js、webpack.prod.js、webpack.dev.js
- 使用 webpack-merge 合并公用部分
- 使用 yarn webpack --config webpack.prod.js 打包生产环境
-
Webpack DefinePlugin
为代码注入全局变量plugins: [ new webpack.definePlugin({ // 值要求的是一个代码片段 // 页面调用此值,直接使用 API_BASE_URL // API_BASE_URL: "'https://api.example.com'" API_BASE_URL: JSON.stringify() }) ]
-
Wepack Tree Shaking & scope Hoisting(合并代码)
生产环境自动开启module.exports = { mode: 'none', entry: './src/index.js', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ // 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效 // ['@babel/preset-env', { modules: 'commonjs' }] // ['@babel/preset-env', { modules: false }] // 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换 ['@babel/preset-env', { modules: 'auto' }] ] } } } ] }, optimization: { // 开启优化 // Tree Shaking 模块只导出被使用的成员 相当于标记枯树枝 usedExports: true, // Tree Shaking 压缩输出结果 相当于摇掉枯树枝 minimize: true, // scope Hoisting 尽可能合并每一个模块到一个函数中 concatenateModules: true } }
-
side-effects
- 标注代码是否有副作用,给 Tree Shaking 提供更大的压缩空间
- 要确保代码没有副作用,否则会误删代码
- 副作用:模块执行时除了导出成员之外所作的事情
- 一般用于 npm 包标记是否有副作用
// webpack.config.js 配置 optimization: { // 开启功能 sideEffects: true } // package.json 配置 // 标识代码没有副作用 // "sideEffects": false // 标识某些文件有副作用 "sideEffects": ["./src/extend.js", "*/.css"]
-
Code Splitting - 分包,按需加载
- 多页面应用,多入口打包
module.exports = { entry: { index: "./src/index.js", album: "./src/album.js" }, plugins: [ new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/index.html', filename: 'index.html', chunks: ["index"] }), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/album.html', filename: 'album.html', chunks: ["album"] }), ], optimization: { // 自动提取所有公共模块到单独 bundle chunks: "all" } }
- 动态导入
- 需要用到的模块才加载
- WebpackChunkName 魔法注释动态输出文件,如果名字相同会输出到同一文件
if (hash === '#posts') { // mainElement.appendChild(posts()) import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => { mainElement.appendChild(posts()) }) } else if (hash === '#album') { // mainElement.appendChild(album()) import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => { mainElement.appendChild(album()) }) }
-
mini-css-extract-plugin CSS 按需加载
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
optimization: {
minimizer: [
// 压缩输出的 CSS 文件
new TerserWebpackPlugin(),
// 由于 minimizer 被覆盖,所以需要加上压缩输出的 JS 文件的功能
new OptimizeCssAssetsWebpackPlugin()
]
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 'style-loader', // 将样式通过 style 标签注入
MiniCssExtractPlugin.loader, // 将样式通过 link 引入
'css-loader'
]
}
]
},
plugins: [
// 提取 CSS 到单个文件,若 CSS 文件小于150KB,不建议提取
new MiniCssExtractPlugin()
]
}
- 输出文件名 Hash
output: {
filename: '[name]-[contenthash:8].bundle.js'
},
其他打包工具
Rollup
仅仅是一个 ES Module 打包器
提供一个充分利用 ESM 各项特性的高效打包器
- 快速上手
yarn add rollup --dev
yarn rollup ./src/index.js --format iife --file dist/bundle.js
-
配置文件
// 新建 rollup.config.js export default { input:"src/index.js", output: { format: "iife", file: "dist/bundle.js" } }
执行 yarn rollup --config
-
使用插件
插件是 Rollup 唯一拓展途径import json from "rollup-plugin-json" export default { input:"src/index.js", output: { format: "iife", file: "dist/bundle.js" }, plugins: [ // 可引入加载 json 文件 json() ] }
-
Rollup 加载 npm 模块
使用 rollup-plugin-node-resolve 插件加载 npm 模块,比如 lodash-es (ES Module 版本的 lodash,因为 Rollup 默认支持 ESM) -
Rollup 加载 CommonJS 模块
使用 rollup-plugin-commonjs 插件 -
Rollup 按需加载
// 动态导入 import("./logger").then(({ log }) => { log("code splitting") })
- 输入格式不能是 iife 只能是 amd
export default { input: "src/index.js", output: { dir: "dist", format: "amd" } }
-
Rollup 多入口打包
export default {
// 数组和对象类型均可
// input: ["src/index.js", "src/album.js"],
input: {
foo: "src/index.js",
bar: "src/album.js"
},
output: {
dir: "dist",
format: "amd"
}
}
- Rollup 选用原则
- 优点
- 输出结果更加扁平
- 自动移除未引用代码
- 打包结果依然完全可读
- 缺点
- 加载非 ESM 的第三方模块比较复杂
- 模块最终打包到一个函数中,无法实现 MHR
- 浏览器环境中,代码拆分依赖 AMD 库
- 总结
- 开发应用程序可以使用 webpack
- 开发框架/类库可以使用 Rollup
- 优点
Rarcel
- 零配置的前端应用打包器
- 自动安装模块
- 采用多进程打包工作,速度较快
yarn add parcel-bundle --dev
yarn parcel src/index.html // 以 index.html 为入口文件运行开发环境
yarn parcel build src/index.html // 打包生产环境
规范化标准
-
规范化介绍
- 为什么要有规范化标准
- 软件开发需要多人协同
- 不同开发者具有不同的编码习惯和爱好
- 不同的喜好增加项目维护成本
- 每个项目或者团队需要明确统一的标准
- 哪里需要规范化标准
- 代码、文档、甚至提交日志
- 开发过程人为编写的成果物
- 代码标准化规范最为重要
- 实施规范化的方法
- 编码前人为的标准约定
- 通过工具实现 Lint
- 为什么要有规范化标准
-
ESLint 介绍
- 最为主流的 Javascript Lint 工具检测 JS 代码质量
- ESLint 很容易统一开发者的代码风格
- ESLint 可以帮助开发者提高编码能力
-
ESLint 快速入手
npm init --yes // 初始化项目
npm install eslint --save-dev // 安装 eslint
npx eslint --init // 初始化 eslint 配置
npx eslint ./src // 代码检查 src 文件
npx eslint ./src --fix // 自动修复 src 文件的有问题代码
-
ESLint 配置文件解析
- env:标记代码运行的环境,例如 browser、node
- extends: 继承共享配置,例如 standard 代表 github 上公用的配置
- parserOptions:设置语法解析器配置,控制允许使用 ES 哪个版本
- rules:检验规则的开启和设置
- globals:代码可以使用的全局成员
-
ESLint 配置注释
- 文档地址
http://eslint.cn/docs/user-guide/configuring#configuring-rules
- 示例
// esline-disbale-line 为固定注释 // no-template-curly-in-string 注释的内容类型 const str1 = "${name} is a coder" // esline-disbale-line no-template-curly-in-string
-
ESLint 结合自动化工具
- 完成相应的依赖安装
- 完成 ESLint 模块安装
- 完成 gulp-eslint 安装
- npx eslint --init // 初始化生成 .eslintrs.js
- 在 gulpfile.js 添加代码
const script = () => { return src("src/assets/scripts/*.js", {base: "src"}) .pipe(plugins.eslint()) .pipe(plugins.eslint.format()) .pipe(plugins.eslint.failAfterError()) // 发现错误则停止 }
-
ESLint 结合 webpack
- 完成相应的依赖、eslint模块、eslint-loader 模块安装
- npx eslint --init
- 在 webpack.config.js 中的 rules 添加如下配置,使得 eslint-loader 先于其他 loader 执行
rules: [ ..., { test: /.js$/, exclude: /node_modules/, use: "eslint-loader", enfore: "pre", // 优先级高于其他 loader 执行 } ]
- 在 .eslintrs.js 中的 extends 添加配置
extends: [ "standard", "plugin:react/recommended" // 为 react 项目添加检查 eslint 配置 ]
-
现代化项目集成
npm install @vue/cli -g
vue create vue-app // 选择 eslint 配置
-
Stylelint认识
- 提供默认的代码检查规则
- 提供 CLI 工具,快速调用
- 通过插件支持 Sass Less PostCSS
- 支持 Gulp 或 Webpack 集成
- 安装:npm install stylelint --save-dev
- 初始化:npx stylelint --init
// 先安装开发依赖,新建 .stylelintrc.js 文件 module.exports = { extends: [ "stylelint-config-standard", "stylelint-config-sass-guidelines" ] }
- 运行:npx stylelint ./index.css
-
Prettier 的使用
- 自动格式化工具
npm install prettier --save-dev
npx prettier . --write // 自动格式化当前所有文件 -
Git Hooks 工作机制
- Git Hooks 也称为 git 钩子,每个钩子都对应一个任务
- 通过 Shell 脚本可以编写钩子任务触发时要具体执行的操作
-
ESLint 结合 Git Hooks
npm install husky -D
npm install lint-staged -D
// 在 package.json 加上如下配置
"scripts": {
"precommit": "lint-staged"
}
"husky": {
"hooks": {
"pre-commit": "npm run precommit"
}
},
"lint-staged": {
"*/.js": ["eslint", "git add"]
}