Webpack4/5学习笔记

为什么要用webpack

1.作用域问题 jquery lodash 自定义对象会绑定window.$ ... 污染window对象

 之前是使用 grunt和gulp 任务执行器(利用了js的立即调用函数表达式 IIFE Immediately Invoked Function Expressions)

2.文件分散加载,页面的内容会随着文件的加载而显示

3.打包在一起:可读性,维护性差

 

构建内库,很少的第三方库,可以用roullup.js

简单命令

1.npm init -y 生成package.json

 

概念

1.webpack静态模块打包器

2.资源 js css less image 都要交给构建工具(webpack)去处理  怎么处理:

  • 告诉webpack起点:入口文件 index.js
  • 将依赖关系记录好(比如index.js中 有jquery 和 less),然后将资源文件引进来,形成代码块 chunk,代码块可以将 less编译成css , ES6编译成ES5   ,统一说成是打包
  • 打包之后 输出的文件 我们叫做 bundle.js

 

 

五个核心概念

1. Entry  入口  指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。

2.Output 输出 指示webpack以打包后的资源bundles 输出到哪里去,以及如何命名。

3.Loader 翻译官 让webpack能够去处理那些非javascript文件(webpack自身只理解javascript)。

4.Plugins 插件 开飞机 开航母 可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

5.mode 

 

 

 

  • 命令中“-o”的含义就是“输出”的意思,后面紧跟结果文件名。 
  • 生成环境会压缩代码
  • 直接使用node就可以运行 built.js文件     
node ./src/built.js

 

webpack.config.js  webpack的配置文件

所有构建工具都是基于node.js平台运行的~模块化默认采用commonjs。

(CommonJS是一种被广泛使用的js模块化规范,核心思想是通过require方法来同步加载依赖的其他模块,通过module.exports导出需要暴露的接口)

样式文件:能够实现HMR功能,是因为style-loader内部实现了
JS文件:默认不能使用HMR功能=>
  解决:需要修改js代码,添加支持HMR功能的代码 (只能热替换入口文件以外的被引入的文件,入口自身不能热替换)
index.js文件代码
在index.js里给需要热模块替换的文件添加代码
import Refresh from '../../../components/RefreshList'
  if(module.hot){
  module.hot.accept ('./RefreshList.js',function(){
  //方法会监听RefreshList.js文件的变化,一旦发生变化,其他模块不会重新打包构建
  //会执行里面的回调函数
  RefreshList()
})
}
RefreshList文件代码
console.log('我被加载了~');
export default RefreshList

HTML文件:默认不能使用HMR功能,同时导致html文件不能热更新了(不用做HMR功能)=>
  解决:修改entry入口,将html文件引入 entry:['./src/js/index.js','./src/index.html']
    

//
resolve用来拼接绝对路径的方法 const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 压缩CSS插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 设置nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'development'
module.exports = { 
// 入口起点
entry:'./src/index.js',
// 输出
output:{

filename:
'built.js',
// __dirname 是nodejs的变量, 代表的是当前文件的目录绝对路径
path:resolve(__dirname,'build')
// 告诉webpack不要使用箭头 
environment: {
  arrowFunction: false
}
},
module:{
  rules:[
// 详细的loader配置
    {
      //语法检查: eslint-loader eslint
      //设置检查规则: package.json中的eslintConfig中设置~
      //airbnb风格: eslint-config-airbnb-base eslint-plugin-import eslint
      test: /.js$/,
      exclude:/node_modules/,
      loader:'eslint-loader',
      options:{
      //自动修复eslint错误
      fix:true

    },
    {
      //js兼容性处理: babel-loader @babel/preset-env @babel/core
      //1.只能转换基本语法 @babel/preset-env 如promise 不能转换
      //2.按需加载 core-js
      test:/\.js$/,
      exclude:/node_modules/,
      loader:'babel-loader',
      options:{
        //预设:指示babel做怎样的兼容性处理
        presets:[
        '@babel/preset-env',
        {
          //按需加载
          useBuiltIns:'usage',
          //指定core-js版本
          corejs:{
            version:3
          },
          // 指定兼容性做到哪个版本
          targets:{
            chrome:'60',
            firefox:'60',
            ie:'9',
            safari:'10',
            edge:'17'
          }
        }

        ] //箭头函数会转化为普通函数

      }
    },
    
{
    test:/\.css$/,
   // 执行顺序 : 由后往前
    use:[
      //创建style标签, 将js中的样式资源插入进行,添加到head中生效
      //
'style-loader',
      // 取代style-loader : 提取js中的css成单独文件,创建link标签,引入css
   MiniCssExtractPlugin.loader,
      //将css文件变成commonjs模块加载到js中,里面内容是样式字符串
      
'css-loader',
      // css兼容处理 postcss, 还需在package.json中的browserslist中配置
      {
        loader:'postcss-loader',
        options:{
          // 识别postcss语法
          ident:'postcss',
          plugins:()=>[
          //postcss插件
          require('postcss-preset-env')()
          ]
        }
      }
    ]
   },
   {
    test:/\.less$/,
    use:[
      //创建style标签,将样式放入
      'style-loader',
      //将css文件整合到js文件中
      'css-loader',
      //将less编译为css
      'less-loader'
    ]
   },
   {
    // 问题: 默认处理不了html中的img图片
    // 处理图片资源
    test:/\.(jpg|png|gif)$/,
    // 下载url-loader file-loader
    
loader:'url-loader',
    options:{
     // 图片小于8kb,就会被base64处理
// 优点: 请求数量减少
// 缺点: 图片体积会更大
      limit: 8 * 1024,
      // 问题: 因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
      // 解析时会出问题: [object Module]
      // 解决: 关闭url-loader的ES6模块化, 使用commonjs解析
      esModule:false
      // 给图片进行重命名
      // [hash:10]取图片的hash前10位
      // [ext]取文件原来的拓展名
      name:'[hash:10].[ext]'
    }
   },
   {
    test:/\.html$/,
    //处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
    loader:'html-loader'
   },
      // 打包其他资源(除了html/js/css资源以外的资源)
        {
          //排除css/js/html资源
          exclude: /\.(css|js|html|sass)$/,
          loader:'file-loader'
        }
    ]
  },
  plugins:[
    // 详细plugins配置
   // html-webpack-plugin
// 功能:默认会创建一个空的HTML, 自动引入打包输出的所有资源(js/css)
// 需求:需要有结构的HTML文件
new HtmlWebpackPlugin({
    // 复制'./src/index.html' 文件, 并自动引入打包输出的所有资源(JS/CSS) 会自己创建script标签 并引入资源
template:'./src/index.html',
    //压缩html代码
    minify:{
    //移除空格
    collapseWhitespace:true,
    //移除注释
    removeComments:true
  }
  
}),
   new MiniCssExtractPlugin({
    //配置提取后的css文件路径
    filename:'css/built.css'
  }),
  // 压缩css
   new OptimizeCssAssetsWebpackPlugin()

], // 模式 mode:'development',
//压缩js代码
// mode:'production'
 
 // 开发服务器 基于node.js搭建,内部使用了express框架 devServer: 用来自动化(自动编译、自动打开浏览器、自动刷新浏览器)
 // 特点:只会在内存中编译打包,不会有任何输出。
 // 启动devServer指令为: npx webpack-dev-server (因为是本地的包,所以要npx启动)
 devServer:{
  contentBase:resolve(__dirname,'build'),
 // 启动gzip压缩
  compress:true,
 //端口号
  port:3000,
  open:true,
  //开启HMR功能
  hot:true,
  //页面实时刷新
  inline:true
 },z
 //配置路径的规则,可以配置引入资源的默认路径
 resolve: {
  // 配置别名
  alias: {
    style: path.resolve(__dirname,'src/style')
  }
// 配置省略文件路径后缀名
extensions:['.ts','.js']
 }
}

 

package.json

"browserlist":{
  "development":[ // 开发环境 兼容最近的浏览器,如果要使用开发环境 要设置node环境变量: process.env.NODE_ENV = 'development'
   "last 1 chrome version", 
  "last 1 firefox version",
  "last 1 safari version",
], "production":[ // 生产环境:webpack默认生产环境
  ">0.2%", //大于99.8%的浏览器
  "not dead", //不要兼容已经死了的浏览器 比如ie10
  "not op_mini all"
] },
"eslintConfig":{
  "extends":"airbnb-base" //配置eslint
}

 

 

进阶

减小JS文件大小:mini-css-extract-plugin 提取CSS文件

css兼容性处理: post--> postcss-loader postcss-preset-env (postcss 要在webpack中运行,需要这两个插件)

开发环境调试代码:source-map

 

当我们执行打包命令后,我们发现bundle的最后一行总是会多出一个注释,指向打包出的bundle.map.js(sourcemap文件)。 sourcemap文件用来描述源码文件和 bundle 文件的代码位置映射关系。基于它,我们将bundle文件的错误信息映射到源码文件上。
除开’source-map’外,还可以基于我们的需求设置其他值,webpack——devtool一共提供了7种SourceMap模式:

eval:每个module会封装到 eval 里包裹起来执行,并且会在末尾追加注释 //@ sourceURL.
source-map:生成一个SourceMap文件
hidden-source-map:和 source-map一样,但不会在 bundle 末尾追加注释
inline-source-map:生成一个 DataUrl 形式的 SourceMap 文件.
eval-source-map:每个module会通过eval()来执行,并且生成一个DataUrl形式的 SourceMap
cheap-source-map:生成一个没有列信息(column-mappings)的SourceMaps文 件,不包含loader的 sourcemap(譬如 babel 的 sourcemap)
cheap-module-source-map:生成一个没有列信息(column-mappings)的SourceMaps文件,同时 loader 的 sourcemap 也被简化为只包含对应行的。
要注意的是,生产环境我们一般不会开启sourcemap功能,主要有两点原因:

1.通过bundle和sourcemap文件,可以反编译出源码————也就是说,线上产物有soucemap文件的话,就意味着有暴漏源码的风险。
2.我们可以观察到,sourcemap文件的体积相对比较巨大,这跟我们生产环境的追求不同(生产环境追求更小更轻量的bundle)。


 

 

技巧

复用loader

const commonCssLoader = [

  MiniCssExtractPlugin.loader,
      //将css文件变成commonjs模块加载到js中,里面内容是样式字符串
      'css-loader',
      // css兼容处理 postcss, 还需在package.json中的browserslist中配置
      {
        loader:'postcss-loader',
        options:{
          // 识别postcss语法
          ident:'postcss',
          plugins:()=>[
          //postcss插件
          require('postcss-preset-env')()
          ]
        }
      }
]

rules:[
{
  test:/.\css$/,
  use:[...
commonCssLoader]
},
{
  test:/\.less$/,
  use:[...commonCssLoader,'less-loader']
}
]

 

 优化

开发环境

  • 优化打包构建速度
  • 优化代码调试

问题:修改了CSS ,整个文件都重新打包了

解决:HMR(hot module replacement) 热模块替换 / 模块热替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 能够极大的提升构建速度

生产环境

  • 优化打包构建速度
  • 优化代码运行的性能

 

常见的代码分离方法:

1.入口起点:使用entry配置手动地分离代码

// entry: resolve(__dirname, './src/index.js'),
entry: {
  index: resolve(__dirname, './src/index.js'),
  another: resolve(__dirname, './src/another.js'),
},

Entry 进行代码分离的话,每个引入lodash的文件都会把lodash打包一次,如果5个文件引入,那么打包后的体积就会大 553KB * 5

定义公共文件

entry: {
  index: {
    import: resolve(__dirname, './src/index.js'),
    dependOn: 'shared'
  },
  another: {
    import: resolve(__dirname, './src/another.js'),
    dependOn: 'shared'
  },
  shared: 'lodash'
}

2.防止重复:使用Entry dependencies 或者 SplitChunksPlugin 去重和分离代码

optimization: {
// 抽离公共文件
  splitChunks: {
    chunks: 'all'
  }
},

3.动态导入:动态导入,通过模块的内联函数调用来分离代码

 

example1. async-module.js

async function getComponent() {
  // 返回一个Promise
  return import('lodash').
    then(({ default: _ }) => {
      const element = document.createElement('div');

      element.innerHTML = _.join(['A', 'B', 'C'], '<=>')

      return element;
    })
}

getComponent().then((element) => {
  document.body.appendChild(element);
})
import './async-module';

example2:

const button = document.createElement('button');

// webpackChunkName 魔法注释,修改打包后的文件名 button.textContent
= 'add'; button.addEventListener('click', () => { import(/* webpackChunkName: 'changeName' webpackPrefetch: true */'./math').then(({ add }) => { button.textContent = add(10, 10); }) })
 
// webpackPrefetch: true 预加载:当页面的内容都加载完毕,在网络空闲的时候,再去加载我们打包好的math.bundle.js, 比懒加载优秀

 

 

 

缓存第三方库

optimization: {
  splitChunks: {
    // 缓存第三方库: 将所有的第三方库打包到一个文件中,在浏览器中缓存,由于这个库不频繁更新,可以提升我们的首屏打开速度和节省我们的流量
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'cacheLibraries',
        chunks: 'all'
      }
    }
  }
}

 

一、常用操作/指令

mkdir 创建一个子目录

mv (move的缩写) 移动

 

// babel-loader 在webpack里应用babel解析ES6的桥梁
// @babel/core babel核心模块
// @babel/preset-env babel预设,一组babel插件的集合
// @babel/runtime babel运行时需要
// @babel/plugin-transform-runtime  在需要regeneratorRuntime的地方,自动require导包

 

安装babel

yarn add babel-loader @babel/core @babel/preset-env -D

安装eslint

yarn add eslint eslint-loader  -D

配置eslintrc.json ?  

在这个网站 https://eslint.org/demo 直接下载

二、webpack.config.js 核心概念

entry: 一个可执行模块或库的入口文件。
output: 出口文件名
chunk :多个文件组成的一个代码块,例如把一个可执行模块和它所有依赖的模块组合成一个 chunk 这体现了webpack的打包机制。
loader :文件转换器,例如把es6转换为es5,scss转换为css。
plugin :插件,用于扩展webpack的功能,在webpack构建生命周期的节点上加入扩展hook为webpack加入功能。

 

module.exports = {
  entry: {
    bundle1:'./main1.js',
bundle2:'./main2.js'
  },
  output:{
    filename:'[name].js'
  }
}

 三、Code Splitting

在最开始使用Webpack的时候, 都是将所有的js文件全部打包到一个build.js文件中(文件名取决与在webpack.config.js文件中output.filename), 但是在大型项目中, build.js可能过大, 导致页面加载时间过长. 这个时候就需要code splitting, code splitting就是将文件分割成块(chunk), 我们可以定义一些分割点(split point), 根据这些分割点对文件进行分块, 并实现按需加载.


 

遇到的问题:

首先是正常启动服务,编译成功,但是打包后apk运行失败

然后重新拉取代码,安装包后报下面的错

1.编译报错

 

 查找报错原因:

 

1. 查看依赖版本和原来正常运行的APP之间版本区别

 

2.删除掉index.js和APP.js中的代码 ,随便写个console.log, 看看是不是react-redux的问题

此方法替换后,还是报错。

 

于是把成功运行的APP的node_modules包复制过来,成功运行和打包

 

总结: 重新拉取代码,启动程序,程序可以启动的话,才能去编译。

不要因为前面可以启动程序,就想当然的以为重新拉取的项目也可以启动。

后来发现报这个错误 

老项目可能修改过node_modules里面的文件,所以重新安装的就没用

 

 

  • 使用.env.development/.env.production设置环境变量

目录结构

 

 

 

原理:
1,利用node的fs模块读取文件处理成对象
2,用webpack.DefinePlugin插件,设置process.env

 

create-react-app eject 后 有个env.js文件,用来读取环境变量文件并转化为对象

/* 像vue-cli3 新版create-react-app 一样规定环境变量的Key必须以(VUE_APP_) (REACT_APP_) 开头 */

 

.env.development

# 是否启动浏览器
BROWSER=none
# 缓存的命名空间
REACT_APP_STORE_NAMESPACE=HNAJYJZH
# 引入依赖脚本
REACT_APP_INCLUDES_JS=
# 初始化脚本
REACT_APP_MAIN_JS=<script src="./main/web.js"></script>

.env.production

GENERATE_SOURCEMAP=false
INLINE_RUNTIME_CHUNK=false
PUBLIC_URL=./
# 缓存的命名空间
REACT_APP_STORE_NAMESPACE=HNAJYJZH
# 引入依赖脚本
REACT_APP_INCLUDES_JS=<script src="cordova.js"></script>
REACT_APP_MAIN_JS=<script src="./main/mobile.js"></script>

 

public文件夹下的index.html

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="应用首页" />
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  <title>APP首页</title>
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
  %REACT_APP_INCLUDES_JS%
</body>
%REACT_APP_MAIN_JS%

</html>

压缩前的web.js文件

import axios from 'axios';
import qs from 'qs';
import toLower from 'lodash/toLower';
import get from 'lodash/get';

const API_BASE_URL = '/hnajyjzh-ide/';

/**
* WEB端底层API桥接封装
* @date 2021-4-11 01:15:53
* @author Leon<leonooo@163.com>
*/

// 服务器端API接口

// 后台接口底层实现
function request(params) {
    const method = toLower(get(params, 'method', 'get'));
    axios({
        url: API_BASE_URL + params.url,
        method,
        timeout: 1000 * 10,
        headers: params.headers,
        data: method === 'post' ? qs.stringify(params.data) : params.data,
    }).then((data) => {
        params.success(data);
    }).catch((err) => {
        params.error(err);
    });
}

function onload() {
    window.emitter.on('request', request);
    window.emitter.emit('render', {
        baseUrl: API_BASE_URL,
        vconsole: false,
    });
}

window.addEventListener('load', onload, false);

abc.js文件 复制压缩后的文件到变量中的文件夹下

const path = require('path');

module.exports = {
    mode: process.env.NODE_ENV || 'production',
    entry: './bridges/web.js',
    output: {
        filename: 'web.js',
        path: path.resolve(__dirname, '../public/main'),
    },
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: { presets: ['@babel/preset-env'] },
        }],
    },
};

copy.js

const path = require('path');
const fs = require('fs-extra');

const www = path.join(__dirname, '../../app-cordova/www');
const build = path.join(__dirname, '../build');
const public2 = path.join(__dirname, '../public');

fs.removeSync(www);
// 创建目录
fs.ensureDirSync(path.join(build, 'main'));
// 拷贝文件 拷贝a文件 给 b文件
fs.copyFileSync(path.join(public2, 'main/mobile.js'), path.join(build, 'main/mobile.js'));
fs.copyFileSync(path.join(public2, 'main/web.js'), path.join(build, 'main/web.js'));
// 复制文件或目录 该目录可以包含内容
fs.copySync(build, www);
// 删除文件或目录 该目录可以包含内容 如果该路径不存在,则静默不执行任何操作。
fs.removeSync(path.join(www, 'main/web.js'));
fs.removeSync(path.join(www, 'robots.txt'));

package.json 命令

  "scripts": {
    "build": "react-scripts build",
    "www": "run-s bridge:* copy",
    "copy": "node ./scripts/copy.js",
    "format": "eslint ./src --fix",
    "update": "ncu",
    "bridge:web": "webpack --config scripts/abc.js",
    "bridge:app": "webpack --config ./scripts/webpack.config.mobile.js",
    "prepare": "cd .. && husky install ui/.husky"
  },

 

进行大量的运算,又不阻塞主线程: 首先考虑的是异步,但是也可能导致浏览器假死状态

允许我们将耗时的,单纯的js处理逻辑放在后台处理

升级笔记

autoprefixer

是个前端都能懂,就是css自动加前缀,配合postcss使用,如果想更加详细的postcss配置,请移步移动端适配

html-webpack-plugin

这个插件就是为你生成一个已经自动注入打包后的js的html文件

case-sensitive-paths-webpack-plugin

这个插件就是防止不同的系统下对于大小写的问题导致路径出错

InterpolateHtmlPlugin

这个插件是配合html-webpack-plugin一起使用的,允许你在index.html中使用变量

  // 我们在create-react-app生成的项目中public下的index.html可以看到下面的代码
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">

这里的PUBLIC_URL就是一个变量,当webpack打包的时候将你设置的值替换它。

WatchMissingNodeModulesPlugin

这个也是方便开发的插件,当你在一个项目里引用了没有安装的node_modules,webpack会报错module not found,然后你安装完成后,需要自己手动重新启动服务。WatchMissingNodeModulesPlugin这个插件就是帮你省去这步骤,你安装玩缺失的module,webpack会自动重启。

eslintFormatter

emmmm....eslint

ModuleScopePlugin

这个插件是防止你在当前项目中引入非本项目的资源(即src和node_modules文件夹之外的),因为只有src文件夹下的代码才会被babel转译。

webpack.NamedModulesPlugin

这个插件的作用是在热加载时直接返回更新文件名,而不是文件的id,方便开发人员定位。具体

webpack.DefinePlugin

DefinePlugin 允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。比如我们区分测试环境和正式环境的域名

  new webpack.DefinePlugin({
    PRODUCTION: process.env.NODE_ENV === "production",
    DEVELOPMENT: process.env.NODE_ENV === "development"
  })
复制代码

然后在js代码中,如果代码中使用了eslint,还需要在package.json中将这个变量声明一下

 "eslintConfig": {
    "extends": "react-app",  // 继承已启用的规则配置
    "globals": {
      "PRODUCTION": false  // false不允许PRODUCTION变量重写,只读属性
      "DEVELOPMENT": true  // true允许DEVELOPMENT变量重写
    }
  }
 
 if(PRODUCTION){
    domain = www.production.com
  }else{
    domain = www.development.com
  }
 

webpack.HotModuleReplacementPlugin

模块热替换插件,cereate-react-app中只有css变换才有热替换,js变更会刷新页面。

IgnorePlugin

webpack打包时忽略打包的资源。

webpack.config.prod.js

extract-text-webpack-plugin => mini-css-extract-plugin

    => 

 

 

 

将CSS从JS bundle中拆分出来,减小JS的文件大小,加载速度更快, 并合并成一个css文件,并改为link的方式引入

webpack-manifest-plugin

这个就是在build打包的过程中生成一个JSON文件,用来展示编译之前得文件和编译以后的文件的映射关系。

sw-precache-webpack-plugin

SWPrecacheWebpackPlugin是一个webpack插件,用于使用service worker来缓存外部项目依赖项。

 

PWA 渐进式网络应用程序:利用serve works的一个web技术
 

初步了解 Service Worker

深入了解 Service Worker

 

其他知识:

/*#__PURE__*/

webpack压缩(tree-shaking树摇)的时候,如果看到/*#__PURE__*/这个标志,说明他是纯函数,如果没有调用它,会直接把它删除了,减少代码体积

optionalDependencies (可选依赖)


如果一个依赖关系可以被使用,但你希望npm在找不到它或安装失败的情况下继续进行,那么你可以把它放在optionalDependencies对象中。例如你使用mac os系统 并在项目中安装了依赖a,但是你团队中有其他成员使用Linux系统进行开发, 然而在Linux系统中并不存在a这个包, 这种情况下就可以使用optionalDependencies

除此之外, 如果有一些依赖包即使安装失败,项目仍然能够运行或者希望npm继续运行,就可以使用optionalDependencies。另外optionalDependencies会覆盖dependencies中的同名依赖包,所以不要在两个地方都写。

 

posted @ 2020-08-08 16:53  一路向北√  阅读(254)  评论(0编辑  收藏  举报

web应用开发&研究 -

业精于勤而荒于嬉。

工作,使我快乐。


Font Awesome | Respond.js | Bootstrap中文网