相关文章推荐
伤情的脸盆  ·  react-router ...·  2 天前    · 
还单身的蘑菇  ·  react ...·  2 天前    · 
严肃的跑步鞋  ·  React 使用 Proxy ...·  昨天    · 
纯真的领结  ·  npm ...·  5 小时前    · 
伤情的消防车  ·  jquery preload ...·  1 年前    · 
热情的八宝粥  ·  node.js - nvm use ...·  1 年前    · 
大力的镜子  ·  python - django error ...·  1 年前    · 
依赖包 描述
@babel/core 调用babel api进行转码的核心库,babel-loader的核心依赖
babel-loader webpack编译js文件的loader

3. html文件输出

npm i html-webpack-plugin --save-dev

根目录新建index.html和favicon.ico

src目录新增入口文件(index.js)

document.querySelector('#root').innerHTML='<h1>Hello,Javascript</h1>';

2.webpack相关基础配置

  • 根目录下新建config目录
  • 统一管理node环境所有目录(paths.js)
  • const path = require('path');
    const fs = require('fs');
    const appDirectory = fs.realpathSync(process.cwd()); //项目根目录
    const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
    module.exports = {
      rootDir: appDirectory,
      appIndex: resolveApp('src/index'), //入口文件
      appSrc: resolveApp('src'), //项目代码主目录
      appDist: resolveApp('dist'), //打包目录
      appHtml: resolveApp('index.html'), //模板文件
      appPages: resolveApp('src/pages'),
      appStatic: resolveApp('src/static'),
      appUtil: resolveApp('src/util'),
      appInterfaces: resolveApp('src/interfaces'),
      apis: resolveApp('src/apis'),
      appNodeModules: resolveApp('node_modules'),
    
  • 基础配置文件(webpack.config.js)
  • const webpack = require('webpack');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const paths = require('./paths');
    module.exports = function (webpackEnv) {
      const host = process.env.HOST || '0.0.0.0';
      const isEnvDevelopment = webpackEnv === 'development';
      const isEnvProduction = webpackEnv === 'production';
      let webpackConfig = {
        mode: isEnvProduction ? 'production' : 'development',
        target: 'web',
        entry:{
          app: paths.appIndex
        output: {
          path: paths.appDist,
          publicPath: '/',
          filename: `static/js/[name]${isEnvProduction ? '.[contenthash:8]' : ''}.js`,
          clean: true,
        module: {
          rules: [
              test: /\.jsx?$/,
              loader: 'babel-loader',
              include: paths.appSrc,
        plugins: [
          new HtmlWebpackPlugin(
            inject: true,
            filename: 'index.html',
            template: paths.appHtml,
            favicon: 'favicon.ico',
        devServer: {
          host,
          allowedHosts: 'all',
          compress: true,
          port: 9006,
          historyApiFallback: true,
          open: false,
          hot: true,
      return webpackConfig;
    
  • 用于启动开发环境的配置文件(start.js)
  • const configFactory = require('./webpack.config');
    const config = configFactory('development');
    module.exports = config;
    

    至此,最基础的版本已完成,目录如下:

    ├─ .gitignore ├─ config │ ├─ paths.js │ ├─ start.js │ └─ webpack.config.js ├─ favicon.ico ├─ index.html ├─ package-lock.json ├─ package.json └─ src └─ index.js

    运行webpack server --config config/start.js可在浏览器看到

    3.加入React

    1. react相关依赖

    npm i react react-dom --save
    
    依赖包描述
    reactreact的核心库
    react-dom从react中剥离的涉及DOM操作的部分

    react:react的核心思想是虚拟DOM;主要包括:React.createElement生成虚拟DOM,Component相关的React.createClass,React.Component,React.Children

    react-dom:v0.14+从react核心库中拆离;负责浏览器和DOM操作。还有一个兄弟库react-native,用来编写原生应用。

    react-dom主要包括方法有:

    方法描述
    render将虚拟DOM渲染到真是DOM中
    hydrate服务端渲染,避免白屏
    unmountComponentAtNode从 DOM 中移除已装载的 React 组件
    findDOMNode访问原生浏览器DOM
    createPortal渲染react子元素到制定的DOM中

    2. react根组件

  • 项目入口文件(index.js)
  • import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    ReactDOM.render(<App />, document.getElementById('root'));
    
  • 项目根组件(App.js)
  • import { hot } from 'react-hot-loader/root';
    import React, { Component } from 'react';
    class App extends Component {
      render() {
        return <h1>hello-react</h1>;
    export default hot(App);
    

    3. babel预设解析react

    什么是preset,可以理解为是一系列plugin的集合

    npm i @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties --save-dev
    

    presets 使用babel需要安装的一组插件(也就是支持哪些语法转换成es5)

    presets描述
    @babel/preset-envES语法分析包,根据运行环境对代码做相应的编译
    @babel/preset-react编译react语法
    @babel/plugin-proposal-class-properties支持class语法插件

    像上面的react语法如果没有@babel/preset-react,则会报错↓

    babel7发布后的变化:

  • 弃用年份preset和babel-preset-stage-x(stage-0/1/2/3/4)
  • @babel/preset-env替换之前所有的babel-prese-es20xx和babel-preset-stage-x,提案的5个阶段,0表示只是一个想法,4表示已完成

    重命名:Scoped Packages(@babel/x) 解决:命名困难;是否被他人占用;区分官方包名

    重命名:-proposal-

    任何提案都将被以 -proposal- 命名来标记他们还没有在 JavaScript 官方之内。

    所以 @babel/plugin-transform-class-properties 变成 @babel/plugin-proposal-class-properties,当它进入 Stage 4 后,会把它命名回去。

    Babel 几乎可以编译所有最新的 JavaScript 语法(例如箭头函数、const、对象解构),但对于 APIs 来说却并非如此。例如: Promise、Set、Map 等新增对象,Object.assign、Object.entries等静态方法。这时候就需要polyfill来解决了。

    APIs-polyfill方案

    为了达成使用这些新API的目的,社区又有2个实现流派:@babel/polyfill和babel-runtime+babel-plugin-transform-runtime

  • @babel/polyfill用来处理APIs的兼容问题;通过修改全局变量的方式实现。
    如:[1,2,3].includes(1) 如果不进行兼容处理,打包出来就是直接是调用Array原型includes方法,在低版本浏览器可能会有兼容问题。
  • 安装和配置:

    npm install --save core-js@3 @babel/polyfill
    
    "presets": [
          "@babel/preset-env",
            "modules": false,
    +       "useBuiltIns": "usage", //设置entry,需要手动在代码顶部导入@babel/polyfill,否则没有polyfill;usage 会分析项目使用了哪些自动导入相应的polyfill
    +       "corejs": 3
    

    下面两张图可以看出,core-js通过修改Array的原型对象对数组扩展; 这样会带来一个问题,如果第三方库也修改了原型方法,可能会导致冲突

    注意:如果没有安装core-js的话,打包会报错

    问题2:babel转义js代码需要,会在每个bundle中生成工具函数,如果bundle较多时,会额外增加代码体积(这个问题不是使用core-js导致的问题,是babel转义时产生的)

    辅助函数代码如下:

    为了解决以上两个问题,有了方案2

  • @babel/plugin-transform-runtime
  • @babel/plugin-transform-runtime插件作用是,如发现源码中有Promise、Map等新类型时自动按需加入合适的polyfill以解决兼容问题(不需要每个模块手动导入import Promise from 'babel-runtime/core-js/xxx')。同时解决了使用@babel/polyfill导致的污染全局变量和辅助代码复用的问题。需要用到两个依赖包@babel/runtime-corejs3:加载必要的新API;@babel/runtime:提供帮助程序。

    //注意:用方案2就不需要方案1的两个包
    npm install --save-dev @babel/plugin-transform-runtime
    npm install --save @babel/runtime @babel/runtime-corejs3
    
    "plugins": [
          "@babel/plugin-transform-runtime",
            "corejs": 3
    

    这里的路径最好用绝对路径,在项目中就不会存在替换后的路径问题了

    import bridge from '@util/bridge'; 
    //'@util/bridge'等价于'{项目在计算机中的目录地址}/react-pro/src/util'
    
  • extensions(扩展名) 在导入路径中如果没有文件后缀,webpack会自动加上后缀尝试查找文件是否存在,resolve.extensions用于配置在尝试过程中用到的后缀列表,默认是:
  • extensions: ['.js', '.json']
    

    这里由于新加了react,所以需要修改默认配置为

    extensions: ['.js', '.jsx', '.json']
    

    4.资源文件配置

    1.样式(less,scss,css)文件配置

    项目实际开发中还需要样式文件

    细节可以参考另一篇webpack配置- 样式类,下面是loader配置

    1. 安装依赖包

    npm i node-sass sass-loader postcss-loader autoprefixer css-loader style-loader mini-css-extract-plugin --save-dev

    2.新增loader和plugins

    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const getStyleLoaders = ({modules = true}) =>
      isEnvProduction ? MiniCssExtractPlugin.loader : 'style-loader',
        loader: 'css-loader',
        options: {
          modules: modules ?  {
            localIdentName: '[name]-[local]-[hash:base64:5]',
          } : false,
      'postcss-loader',
      'sass-loader',
    ].filter(Boolean);
    module: {
        rules:[
              test: /\.(sc|c)ss$/,
              use: getStyleLoaders({ modules:true }),
              include: paths.appSrc,
    plugins:[
        isEnvProduction && new MiniCssExtractPlugin({
          filename: 'static/css/[name].[contenthash:10].css',
    ].filter(Boolean)
    

    3.postcss配置

    //postcss.config.js
    module.exports = {
      plugins: autoprefixer: {}
    

    4.添加兼容浏览器列表(package.json)

    "browserslist": ["iOS >= 8","> 1%","Android > 4","last 5 versions"]
    

    5.项目入口引入全局样式文件

    import './assets/styles/app.less';

    重新启动项目,可以看到样式已经插入head中

    2.图片和字体类

    参考:webpack5内置静态资源构建能力

    loader新增:

    test: /\.(gif|png|jpe?g|svg)(\?.*)?$/, type: 'asset', generator: { filename: 'static/img/[name][ext]?[hash]', parser: { dataUrlCondition: { maxSize: 10 * 1024, test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, type: 'asset/resource', include: [paths.appSrc], generator: { filename: 'fonts/[name].[hash:7][ext]',

    3.模块热替换

    参考:模块热替换方案-HotModuleReplacementPlugin(HMR)

    npm install react-router-dom --save
    

    首先当然是依赖包,为什么是react-router-dom而不是react-router
    react-router4.0.0+版本;官方提供了一套基于react-router封装的(包含react-router所有内容)用于运行在浏览器端的react-router-dom和用于开发reactNative应用的react-router-native

    React Router中有三类组件

  • router组件(BrowserRouter,HashRouter)
  • route matching组件(Route,Routes)
  • navigation组件(Link)
  • Router设置

    基于React Router的web应用,根组件应该是一个router组件;react-router-dom提供了两种路由模式;
    <BrowserRouter>:使用HTML5 提供的 history API (pushState, replaceState 和 popstate 事件),动态展示组件
    <HashRouter>:通过监听window.location.hash的改变,动态展示组件

    最直观的感受就是BrowserRouter不会再浏览器URL上追加#,为了地址的优雅当然首选这种模式,但如果是静态服务器,那就只能使用备选方案HashRouter了。

    Route路由匹配

  • React Router提供两个匹配路由的组件<Route><Routes>
  • 路由匹配是通过将组件的path属性与当前locationpathname进行匹配,当一个组件匹配了,则展示

    我们可以在组件树的任何位置放置<Route>组件。但是更常见的情况是将几个<Route>写在一起。<Routes>组件可以用来将多个<Route>“包裹”在一起。

  • useRoutes JavaScript 对象的配置
  • 新建路由组件(src/routes/index.js)。

    <BrowserRouter>
        <Routes>
          <Route path="/" element={<Home/>} />
          <Route path="/shopping" element={<Shopping/>} />
          <Route path="/detail/:id" element={<Detail/>} />
          {/* 如果上面的Route的路径都没有匹配上,则 <NoMatch>被渲染,我们可以在此组件中返回404 */}
          <Route path="*" element={<NoMatch/>} />
        </Routes>
    </BrowserRouter>
    

    useSearchParams获取路由参数

    Link 导航组件

    //to: string
    <Link to="/about?tab=name" />
    //to: object
      to={{
        pathname: "/courses",
        search: "?sort=name",
        hash: "#the-hash",
        state: { fromDashboard: true } //传入下一个页面额外的state参数
    

    useNavigate以编程方式导航

    在不同的React版本中,使用方法稍有差异,下面总结了各版本的使用方法

  • ReactRouter 6 (using hooks and React >16.8) 你可以使用 useNavigate hook 在函数组件中使用编程时导航,ReactRouter5使用useHistory
  • import { useNavigate } from "react-router-dom";
    function HomeButton() {
      let navigate = useNavigate();
      navigate('/some/path', { replace: true })
    
  • React-Router 4.0.0+ 在4.0+, 在组件中使用props中的 history 对象
  • class Example extends React.Component {
       // use `this.props.history.push('/some/path')` here
    
  • React-Router 3.0.0+ 在3.0+, 在组件中使用props中的 router.
  • class Example extends React.Component {
       // use `this.props.router.push('/some/path')` here
    

    5. 状态管理

    npm install redux react-redux --save
    

    redux是一个“可预测的状态容器”,参考了flux的设计思想,

    Redux三大原则

    单一数据源
    一个应用只有唯一的数据源,好处是整个应用的状态都保存在一个对象中,这样可以随时去除整个应用的状态进行持久化;当然如果一个复杂项目也可以用Redux提供的工具函数combineReducers对数据进行拆分管理。

    状态是只读的
    React并不会显示定义store,而使用Reducer返回当前应用的状态(state),这里并不是修改之前的状态,而是返回一个全新的状态。
    React提供的createStore方法会根据Reducer生成store,最后可以用store.disputch方法修改状态。

    状态修改均由纯函数完成
    这使得Reducer里对状态的修改变得简单、纯粹

    Redux核心API

    Redux的核心是一个store,这个store由Redux提供的createStore(reducers[,initalState])方法生成。
    reducers必传参数用来响应由用户操作产生的action,reducer本质是一个函数,其函数签名为reducer(previousState,action)=>newState;reducer的职责就是根据previousState和action计算出新的state;在实际应用中reducer在处理previousState时,需要有一个非空判断。很显然,reducer第一次执行的时候没有任何previousState,而reducer的职责时返回新的state,因此需要在这种特殊情况返回一个定义好的initalState。

    与React绑定

    Redux 官方提供的 React 绑定-react-redux。这是一种前端框架或类库的架构趋势,即尽可能做到平台无关。 react-redux提供了一个组件和一个API,一个是React组件,接受一个store作为props,它是整个Redux应用的顶层组件;一个是connect(),它提供了在整个React应用的任意组件中获取store中数据的功能。

    项目使用redux

  • 入口文件加入Provider组件
  • import { Provider } from 'react-redux';
    import store from './redux/index';
    ReactDOM.render(<Provider store={store}><App /></Provider>, rootEl);
    
  • 创建store文件
  • import reducers from './reducers/index'
    export default createStore(reducers);
    
  • 创建reducers文件
  • export default (state=[],action)=>{
     switch (action.type){
       case 'RECEIVE_PRODUCTS':
         return action.products;
       default:
         return state;
    
  • 容器组件中dispatch触发reducers改变state
  • import { connect } from 'react-redux'
    const ProductsContainer = ({products,getAllProducts}) => (
        <button onClick={getAllProducts}>获取数据</button>
    const mapStateToProps = (state) => ({
      products:state.products
    const mapDispatchToProps = (dispatch, ownProps)=> ({
      getAllProducts:() => {
        dispatch({ type: 'RECEIVE_PRODUCTS', [1,2,3]})
    export default connect(mapStateToProps, mapDispatchToProps)(ProductsContainer)
    

    6.环境全局变量

    项目中测试环境和生产环境常常有些全局变量是不同的;最典型的api接口域名部分、跳转地址域名部分; 我们可以在webpack的plugin中设置DefinePlugin:

    //向浏览器环境注入全局变量,非window下
    new webpack.DefinePlugin({
        'process.env': env //env 获取本地的静态文件
    

    但在webpack node环境中还不能区分测试和生产环境,因为webpack build打包向node注入的NODE_ENV都是produiction,所以process.env.NODE_ENV是相同的。

    这里结合cross-env向node环境手动注入一个标记参数NODE_ENV_MARK;package代码如下:

    "scripts": {
        "dev": "cross-env NODE_ENV_MARK=dev webpack-dev-server --config config/start.js",
        "build:test": "cross-env NODE_ENV_MARK=test node config/build.js",
        "build:prod": "cross-env NODE_ENV_MARK=production node config/build.js"
    

    webpack.config.js中根据NODE_ENV_MARK变量获取对应的文件:

    const env = require(`../env/${process.env.NODE_ENV_MARK}.env`);
    

    env目录下添加dev.env.js/test.env.js/production.env.js;文件内容根据实际情况进行编辑

    module.exports = {
      NODE_ENV: '"production"',
      prefix: '"//api.abc.com"'
    

    这样在浏览器环境中就可以使用process.env.prefix变量了。

    到此项目配置基本告一段落,一下是对项目进行的一些优化。

    7.项目优化

    webpack相关优化可以参考优化

    8.项目规范和风格配置

    eslint:代码风格和语法规则检查工具

    ESLint 是一种检验JavaScript代码格式的工具,目标是使代码更加一致并避免错误, ESLint是一个代码限制的工具 注意:这里的格式不仅包含风格,还包括一些最佳实践,(比如for in循环对象时,eslilt就建议用Object.keys(obj),然后循环数组,这样避免for in循环出原型对象)

  • 安装 npm install eslint --save-dev
  • 初始化配置文件 npx eslint --init(根据项目情况选择)
  • module.exports = {
        //脚本运行的环境
        "env": {
            "browser": true,
            "es2021": true
        "extends": [
            "plugin:react/recommended",
            "airbnb"
        //如果未使用ts请用 @babel/eslint-parser 解析
        "parser": "@typescript-eslint/parser",
        "parserOptions": {
            "ecmaFeatures": {
                "jsx": true
            "ecmaVersion": 13,
            "sourceType": "module"
        // 第三方插件为ESLint定义了额外的rules、env、配置
        "plugins": [
            "react",
            "@typescript-eslint"
        //启用的规则
        "rules": {
    

    初始化后会安装一下依赖

  • eslint@8.6.0 JavaScript检查器,eslint核心库
  • @typescript-eslint/parser@5.9.1 Typescript语法的解析器
  • eslint-config-airbnb@19.0.4 Airbnb的ESLint配置,作为一种扩展的共享配置
  • eslint-plugin-import@2.25.4 ES6+ import/export语法校验,防止文件路径和名称拼写错误的问题
  • eslint-plugin-react@7.28.0 分析react特性
  • eslint-plugin-jsx-a11y@6.5.1 检查JSX语法规范
  • eslint-plugin-react-hooks@4.3.0
  • @typescript-eslint/eslint-plugin@5.9.1
  • 这时候运行eslint src/index.tsx 会报错

    项目中使用了typescript,需要安装eslint-config-airbnb-typescript,增强Airbnb的ESLint,其实就是针对ts的一些配置和关闭一些rules

    eslintrc.js更新

    extends: [
      'airbnb',
    + 'airbnb-typescript'
    + parserOptions: {
    +   project: './tsconfig.json'
    + ignorePatterns: ['.eslintrc.js'],
    根目录下新建.eslintignore,配置的目录或文件将不进行eslint格式校验
    
    **/dist/**
    **/node_modules/**
    **/config/**
    

    如果是新项目加入eslint,extends建议使用airbnb,这样会约束你编写出更加优雅的代码,这样渐渐的也就会成为你的编码风格

    如果是老项目加入eslint,extends建议使用"eslint:recommended""plugin:react/recommended"

    这里项目中使用airbnb;这时候运行npx eslint src,会发现有很多类似这种的报错 大部分报错都是编码风格的报错

    可以使用npx eslint src --fix;可以自动修复编码风格问题,(比如使用单双引号singleQuote、行位是否加分号semi);在我理解自动修复不会新增行或者移动代码。
    运行之后,发现还剩下类似这种的报错,剩下的就需要手动修复了 如果是vscode编辑器,可以安装eslint插件并配置,开启保存自动格式化,方便查看报错提示和修改

    "eslint.validate": [
        "javascript",
        "javascriptreact"
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    

    老版本的项目如果配置了resolve.alias,还会出现一下问题: eslint报错找不到路径;安装插件并新增以下配置即可解决

    npm install eslint-plugin-import eslint-import-resolver-alias --save-dev
    settings: {
        'import/resolver': {
          alias: {
            map: [
              ['@redux', paths.appRedux]
              ['@pages', paths.appPages]
              ['@util', paths.util]
    

    但这时你会发现ctrl/command+鼠标左键无法识别路径,开发体验不是很好。

    在根目录新建jsconfig.json

    "compilerOptions": { "baseUrl": ".", "paths": { "@redux/*": ["src/redux/*"], "@pages/*":["src/pages/*"], "@util/*":["src/util/*"]

    stylelint规范sass文件

    npm install --save-dev stylelint stylelint-config-standard-scss
    根目录新增.stylelintrc.js
    
    module.exports = {
      "extends": "stylelint-config-standard-scss", //扩展三方配置
      "ignoreFiles": ["node_modules/**/*.scss"],
      "rules": {
        "selector-pseudo-class-no-unknown": [
          true,
            "ignorePseudoClasses": ["global"]
    

    rules分三类

  • Possible errors 语法类错误 大部分不可 --fix修复
  • Limit language features 限制写法类 比如小数点后默认只能有4位、不允许0后面的单位、透明值不允许百分比,少部分可以--fix修复
  • Stylistic issues 编码风格类 比如16进制颜色指定大小写、url值是否需要引号、大括号开始/结束是否需要换行,此类基本都可以--fix修复
  • 规则列表中如果带有(Autofixable)的,--fix都是可以自动修复的,其他的需要手动修复。

    VSCode 自动格式化配置

    安装ESlintStylelint 插件

    "editor.codeActionsOnSave": { 
        "source.fixAll.eslint": true,
        "source.fixAll.stylelint": true
    "eslint.validate": [ "vue","html","javascript","typescript","javascriptreact","typescriptreact" ], 
    "eslint.alwaysShowStatus": true, 
    "stylelint.validate": [ "css", "less", "postcss", "scss", "vue", "sass" ],
    

    Prettier

    一款用于格式化代码风格的工具,它的设计哲学是:Opinionated,就是强制性用官方的风格,只提供少量的配置选项(20个左右),目的在于停止关于代码风格的争论。

    上面已经用了eslint,为什么还需要引入Prettier呢?在我理解eslint职责在于检测代码是否符合rules规则, 不符合的会给出提示,--fix还可以解决一些风格问题;prettier用于格式化代码风格避免这些报错;当然prettier无法格式化代码质量和语法类问题,这些还需要eslint来解决(手动解决)。

    npm i -save-dev --save-exact prettier
    npm i -save-dev eslint-config-prettier eslint-plugin-prettier
    
    presets描述
    eslint-config-prettiereslintprettier兼容,关闭所有不必要或可能与prettier冲突的规则
    eslint-plugin-prettier运行eslint会静默prettier检查,反馈给eslint显示错误
    prettier-eslintprettier输出传递给eslint --fix
    stylelint-config-prettierstylelintprettier兼容
  • eslint修改配置
  • extends: [
        'airbnb',
        + 'plugin:prettier/recommended' //需放在最后,不然会被其他规则冲掉,此写法已包括在plugins中加prettier
    
  • 配置package.json命令
  • "scripts": {
        "format": "prettier --write \"src/**/*.{js,jsx,scss}\""
    

    运行 npm run format 发现文件已经被格式化,但语法错误和一些代码质量问题还是需要手动修改

    如果出现保存格式化两次,是eslint和prettier冲突了,需在extends中加入覆盖规则

    husky 添加git hooks

    上面配置了检测代码的eslint和stylelint,如何让每次提交的代码都符合规范,还需要借助自动化工具

    npm i -D husky
    npx husky install
    

    执行husky installgit hooks的目录指定为.husky/

    npm install后自动启用hooks

    npm set-script prepare "husky install"
    

    prepareNPM 操作生命周期中的一环,在执行 install 的时候会按生命周期顺序执行相应钩子: NPM7: preinstall -> install -> postinstall -> prepublish -> preprepare -> prepare -> postprepare

  • .husky/中添加hook
  • pre-commit 提交前验证eslint规则
  • "scripts": {
      "prepare": "husky install",
      "lint": "eslint src --ext .jsx && stylelint \"./src/**/*.scss\""  
    
    npx husky add .husky/pre-commit "npm run lint"
    

    执行commit提交会发现报错,并阻止了代码提交,这样可以避免把错误代码提交到线上导致线上报错。 代码报错修复完,即可提交。

    这个方式虽然可以解决提交前校验的问题,但项目庞大后,修改一个文件后,会校验所有的文件,比较费时,lint-staged可以解决这个问题

    npm i -D lint-staged
    
    "lint-staged": {
        "src/**/*.scss": [
          "stylelint --fix"
        "src/**/*.{ts,tsx,js,jsx}": "eslint --fix"
    

    在.kusky文件夹内修改pre-commit

    #!/bin/sh
    . "$(dirname "$0")/_/husky.sh"
    npx --no-install lint-staged
    

    这样commit文件后就只校验修改的文件了

  • commit-msg 规范提交信息
  • npm install --save-dev @commitlint/config-conventional @commitlint/cli
    

    在根目录新建 commitlint.config.js

    module.exports = { extends: ["@commitlint/config-conventional"] };
    

    .husky创建commit-msg hook

    npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
    

    Commit message格式,注意冒号后面有空格

    <type>(<scope>): <subject>
    

    type:用于说明 commit 的类别,只允许使用下面7个标识,也可以自己在配置文件中更改或者扩展。

    type说明
    feat新功能(feature)
    fix修补bug
    docs文档变更(documentation)
    style代码格式(不影响功能,例如空格、分号等格式修正)
    refactor代码重构
    perf性能优化
    build变更项目构建或外部依赖(例:scopes: webpack、gulp、npm)
    test测试
    ci更改持续集成软件的配置
    chore构建过程或辅助工具的变动
    revert代码回滚

    scope:(可选)说明commit影响的范围;在业务项目中可以根据菜单或功能木块划分 subject: commit 的简短描述,不能超过72个字符,且结尾不加英文句号。

    如果type为feat和fix,则该 commit 将肯定出现在 Change log 之中

    交互式commit

    npm i -D commitizen cz-conventional-changelog
    

    package.json

    "config":{ 
        "commitizen":{ "path":"node_modules/cz-conventional-changelog" } 
        "scripts": { "commit": "git-cz",}
    

    提交代码可以执行

    npm run commit
    

    github.com/futurewan/r…

    文章有什么问题欢迎评论区讨论~