webpack学习

300 阅读14分钟

若有错误请大家指出!谢谢

一、webpack的简单介绍

🎈注意:webpack 的核心定义是 "模块打包工具“ ,而并非转译工具

它支持:

  1. ES Module模块引入方式 import A from './a.js'
  2. Common Js 模块引入规范 var A= require("./a.js)
  3. CMD 规范
  4. ADM 规范
//  header.js
	function Header(){
		var dom = document.getElementById('root')
		var header = document.createElement("div")
		headr.innerText = '我是 header'
		dom.append(header)		
	}
	//	commonJS 规范
	//被导出的js文件需要添加
	module.exports =  Header;

	//   ES Module规范
	//被导出的js文件需要添加
	export default Header;
	index.js  我们在这里引入 header.js文件
//  COmmonJS  规范
	var Header = require("./header.js");
//  ES Module规范
	import Header from "./header.js"

小扩展: webpack在最开始的时候,只能打包js文件,随着webpack的不断升级,现在(4.X)已经能打包css,png, jpg等任何你想打包的文件了

// 安装及版本查看
    npm install webpack@4.25.0  -D 
    npx webpack -v    (npx会到nodemodules中查询版本号)

执行npx webpack index.js 即可打包,在根目录下会生成一个dist文件夹,其内部会有一个js 文件,具体的的文件名,可以在webpack.config.js中自行配置

二、webpack的配置文件

🎈webpack的默认配置文件是webpack.config.js

// 为了让output的path,能指定绝对路径 ,需要先引入node 自带的 path模块
const path = require('path')
// common JS的规范写法(module.exports)
module.exports = {
  // entry 代表 需要打包的入口文件, 如果不指定,则使用此处定义的文件。
  // 当没有此文件 (webpack.config.js) 的时候  默认使用 npx webpack 会报错 , 但如果指定某文件进行打包 : npx webpack XXX.js 则可执行
  // 因为 没有配置项 ,则没有默认打包文件 ,即报错; 因此需要在此处配置默认文件
  entry: './index.js',
  
  // output 代表打包后的文件输出路径
  output: {
    
  // filename 代表文件名的key ,值是具体的value
    filename: 'bundle.js',
    
    // path 后面需要跟绝对路径, 为了填写路径, 需要引入node的path模块(最开始已经引入)
    // 第一个参数 :__dirname 代表的意思是 webpack所在的当前目录的路径
    // 第二个参数 :'bundle'   这样一来拼接的路径就是 "打包生成的文件" 的绝对路径了
    
    path: path.resolve(__dirname, 'bundle')
    //   因此现在的流程就是:
    //       0. npx webpack    因为有了默认文件的存在,不再需要指定文件
    //       1. 找到webpack.config.js,寻找entry入口, 发现需要打包的文件是index.js
    //       2. 需要输出的路径是path 拼接得到的绝对路径
    //       3. 输出的文件 文件名是 filename上 指定的 'bundle.js'
    //       4. 在根目录下,多出一个bundle文件夹,内部生成一个打包好的bundle.js  此文件的本质 实际上是index.js
  }
}

注意: 默认情况下,必须指定webpck.config.js文件进行打包,否则会报错。 如果有需要改动文件名,则应使用 "npx webpack --config XXX.js" 指令进行指定某个打包文件的修改:npx webpack --config mywebpack2333.js

这样即可将 mywebpack2333.js 作为配置文件进行打包了


🎈 在package.json中对指令进行统一管理

在package.json文件中,我们可以找到scripts 字段,打包指令过于臃肿,我们可以在此处进行指令简化"scripts":{"bundle": "webpack"}

下一次需要打包的时候,直接输入 npm run bundle 即可

在我们执行 "npm run bundle" 之后:

123123

可以看到已经成功打包了。


三、打包静态图片资源

👀 我们来关注下 webpack.config.js 中的 module 部分

mudule是对于不同的模块的处理规则。

module的rules属性代表不同的处理规则。rules是一个对象。对象的属性有test、use、exclude、include。

    rules:[
    {
      test : /.\png$/,  // 当匹配的对象是以png结尾的文件
      // test : /\.(png|jpg|gif)$/  表示 以png\gif\jpg结尾的文件
      use:{
        // 就调用 loader 中的 file-loader 进行打包,文件同样生成在 dist 文件夹下,
        loader: 'file-loader',
        options:{
          name: '[name].[ext]', 
          // 打包生成的文件名称配置,旨在让生成的文件,后缀与文件名都与源文件保持一致。也可以写成:name: '[name]_[hash].[ext]'  hash  是文件被打包生成的hash值
          // ext 是 placeholder占位符 中的一种,在官网有介绍 ([hash]也是)
          // 
          
          outputPath:'images/'  // 将此类型打包生成的文件放置于 "disat/images" 文件夹下,若没有此配置项,则依然放置于dist文件夹下
        }
      }
    }]    

rules数组中的对象的test用于匹配某种类型的文件,若通过test的匹配,则会执行当下的use:{...}

loader : 指定使用何种类型的loader;

options : name 可以指定文件名; outputPath 指定文件具体的存放路径 文件存储的具体地址。

如果两个都不指定,则会以打包生成的hash值,作为文件名,文件类型不变。位置存放于dist文件夹下。

小扩展: 上面的demo中, 也可以使用 url-loader 进行 转换。 另外在 outputPath 的同级 ,url-loader 还有一个 limit 字段,此字段设置的是一个大小范围。单位是字节。
当小于这个大小的时候,url-loader会将图片 以base64格式,存在bundle.js 中,以减少请求次数。但是当图片大小超过此范围的时候,webpack就会将图片单独打包。
因此范围一定需要合理,否则大图在存在js中,会非常影响渲染速度。白屏时间严重加长。

四、打包样式[上]

注意:新写法

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

2020.1.27


    {
      test : /.scss$/,
      use:{
        loader: [
          'style-loader',
          'css-loader',
          'sass-loader',
          'postcss-loader'  //  用于为css增加厂商前缀  -webkit--XXX  \ -firefox--XXX
        ],
        // loader 的顺序是 自下而上、 自右而左。
        // 因此先使用sass-loader 将scss 文件转化成普通的 css代码,再 由css-loader 理清 css的引用关系 ,最后由style-loader 解读样式代码
      }
    }

当webpack打包样式文件的时候,会遇到css、styls、scss等样式文件。此时的webpack需要借助loader进行打包。因此,我们需要先安装可能出现的loader。

npm i style-loader css-loader -D

css-loader用于理清css文件之间的引入关系。style-loader用于分析css代码

npm i sass-loader node-sass -D

用于将scss文件中的代码 翻译成 普通css代码

npm i postcss-loader -D

用于 为开发者增加厂商前缀 -webkit--XXX \ -firefox--XXX 等 在 postcss-loader 安装完毕之后。需要先在项目根目录下创建一个 postcss.config.js 的postcss配置文件。此文件书写如下:

module.exports = {
  plugins: [
    require("autoprefixer")
  ]
}

当 webpack.config.js 中loader按顺序执行的时候,遇到了postcss-loader,就会去寻找 postcss.config.js 文件。将按照此文件的配置信息,在plugins中发现需要使用autoprefixer。开始下一步的操作

另外 loader 的顺序是 自下而上、 自右而左执行。顺序错误,可能导致无法成功打包或打包失败


五、plugins -- 让打包更便捷

⭐ htmp-webpack-plugin

👍htmlwebpackplugin 会在打包时 帮助你添加一个 index.html, 否则每次需要手动添加就很低能。且添加的同时也会帮助开发者将bundle.js引用到html文件中

先安装plugin cnpm install html-webpack-plugin -D

使用前需要在 webpack.config.js 中 引入 var HtmlWebpackPlugin = require('html-webpack-plugin'); 接着在plugins 字段中,将对象实例化一下 plugins:[new HtmlWebpackPlugin()]

虽然 htmlwebpackplugin帮助我们引入了js文件,但美中不足的是,他并不会帮助我们添加id为root的div。 而我们的项目是通过document.getelementById进行挂载的。我们可以这样对webpack.config.js的plugins字段 进行配置:

意思是,使用在src目录下的index.html文件作为模板,进行html文件的再造(添加bundle.js)

⭐ clean-webpack-plugin

如果我们现在将输出改成dist.js,再进行打包。在dist文件夹下的确能够生成一个dist.js,并且 html-webpack-plugin 也能为我们将html文件中注入dist.js。但是上一次打包生成的bundle.js依然存在。这就使得体验很不好。我们可以借助 clean-webpack-plugin 来完成此操作(删除往次生成的被重命名的文件)

执行 cnpm i -D clean-webpack-plugin

    //旧版本写法:报错!  
        //引入
        `const CleanWebpackPlugin = require('clean-webpack-plugin')` 
        //实例化对象
        `plugins:[new CleanWebpackPlugin(['dist'])] `
        ---
        //    报错提示是: CleanWebpackPlugin is not a constructor
        ---
    //新版本写法:
        const {CleanWebpackPlugin} = require('clean-webpack-plugin')` 
        plugins:[new CleanWebpackPlugin()] `
        
    //这里不用指定plugins 的CleanWebpackPlugin 路径原因是:  会自动删除新生成文件的旧版文件(将生成文件的上一个版本删除)
    

另外使用plugin或其他引入需要注意:横杠连接处一定要用大写,是一种规范。贴一张图(来自博客园博主 - 浮华夕颜):

注意:plugin是由打包顺序的,比如此处的html-webpack-plugin 在打包后执行,而clean-webpack-plugin是在打包前执行

笼统的概括plugin的话,plugin实际上就是在代码执行到某个时刻时,它会自动帮你去做一些事情。这有点类似生命周期的钩子


六、Entry与Output基础配置

🙃在我们实际工作中,很多时候是需要打包多个js文件的,因此在entry中,我们需要这样写:

entry:{ main : './src/index1.js',sub : './src/index2.js' }

而我们的 output 中: OutPut:{output: filename: 'bundle.js', path: path.resolve(__dirname, 'dist')}

难道让两个被打包的文件重名然后报错? 😡当然不能,上文有提及到[name].[ext]的概念,这里就可以这样使用了

OutPut:{output: filename: '[name].[ext]', path: path.resolve(__dirname, 'dist')} // 这里再次复述一遍: [name] 是源文件的文件名、[hash]是文件被打包时候生成的哈希值、[ext]是源文件的后缀。

这样一来,打包结束后,会生成两个文件,且在html-webpack-plugin的帮助下,两个js都会被引入

上图:

七、SourceMap

sourcemap相当于配置一个映射,比如初始代码是a.js  ,a.js被打包过后会生成b.js并引用到html中去,
此时如果a.js中有错,那么打包生成的b.js也是有错的。但是当我们点击开发控制台上的按钮时,控制台只会跳转到b.js的某一行。但是我们需要在a.js中找到错误所在,从而解决根本。

这时候sourceMap,就能完美帮我们解决这个问题。直接跳转到 a.js 出错的地址。且打包结束后会在dist目录下生成一个.map文件,此文件实际就是映射关系

接下来我们来看看如何配置:

八 、 使用 webpackDevServe 提高运行效率

目前来说,我们每修改一次src目录下的代码,就需要重新,npm run bundle 一次。在真实情况下,这样的操作,无疑效率十分低下。

因此我们将webpack 的 package.json文件中写成

"scripts": {"watch": "webpack --watch"}

因此下一次执行的时候,输入 npm run watch ,一旦源文件发生变化,则webpack会自动重新打包文件. 从而起到刷新的作用.

但当我们第一次执行npm run watch 希望自动帮我们执行打包、更新、打开浏览器、模拟服务器特性等操作时。一句简单的npm run watch 就不行了,此时我们需要webpack-devserver 来帮助我们完成更强大的功能。

WebpackDevServer

WebpackDevServer在webpack内部,并没有内置,我们需要先安装它。npm i WebpackDevServer -D

接下来我们只需要在 webpack.config.js 上:

  1. 添加配置devServer:{ contentBase: './dist' },//我们需要让webpack帮助我们启一个服务器,此服务器的位置建立在dist目录下,

  2. 在package.json中scripts配置项中添加:"start": "webpack -dev-server"

现在我们直接执行: npm run start

此时我们可以直接通过浏览器访问8080端口打开网站。

webpackdevserver和watch的好处比较是:webpackdevserver不仅能够监听到文件发生了改变,需要重新打包;更帮助我们同时刷新浏览器,刷新控制台输出的内容!

我们再配置一条:

此配置项一旦确定,我们就能够在npm run start 的同时,自动打开浏览器并访问服务器地址(如localhost:8080端口),避免手动操作了。

注意:webpack-dev-server 执行后并没有生成dist目录,这并不是没有打包成功的表现。而是webpack-dev-server 将打包的内容存在了内存中,因此会进一步提升打包的速度


九、Hot Module Replacement(hmr) -- 热模块替换

我们先将package.jsonscripts的配置项只留下"start": "webpack-dev-server"

使用webpack-dev-server 来讲解

现在我们启动 npm run start

服务器帮我们自动打开了浏览器。但是目前由于我们没有添加任何东西,因此页面上是空白状态。 现在我们来书写index.js中的代码:

style.css代码:

注意此处需要使用到 css-loader 与 stylye-loader webpack的版本不同情况下,也对应着不同写法这里是W4版本:

我们可以看到,在浏览器上:

😎:但有一点问题,在我们修改style.css代码的时候,浏览器会刷新,并且会将我们已经生成的item全部清空,如果想要出现效果,则需要再次一遍遍点击新增按钮. 如果我们只希望它刷新css代码呢(页面之前渲染出的html不变,只有样式改变)?

hmr 登场

我们先到webpack.config.js中,为dev-server项添加 hot: true字段,意为启用热模块更新;安全起见,再添加一条 hotOnly:true 意为即便热模块更新没有启用,那么也阻止浏览器自动刷新

接下来引入一个webpack的plugin 实例化webpack对象:

添加plugin:

上述两项搞定之后最好重启下服务,npm run start,hrm就开启并可以使用了.

这时候我们会发现,改变css后.页面后来渲染出来的html并不会被刷新,只有css被刷新了.偶数项的item变成了红色


十、使用Bable处理es6语法

我们首先将index.js中的代码替换:

接下来我们使用npx webpack 进行打包(不使用webpack-dev-server打包的原因是:此方法会将打包的结果储存在内存里,我们就无法观察有无babel情况下打包的异同)

注意:chrome是可能执行成功的,他已经对es6语法做了技术支持,能够辨认。但如果是一些国产浏览器、ie低版本在没有babel的情况下是一定报错的。

安装babel:

cnpm install --save-dev babel-loader @babel/core

webpack.config.js 添加配置项:

npm i babel-loader @babel/core -D 但此时只有babel-loader还不够, babel-loader只是webpack与babel之间的桥梁。他并不会帮助我们将es6的语句转换成es5的。我们还需要:npm install @babel/preset-env --save-dev

@babel/preset-env 这个模块里,包含了所有 es6 转换成 es5 的规则。

安装完毕后,完成如下配置就能将所有es6转换成es5了。

    { 
      test: /\.js$/,  // 当匹配到 不属于node_modules下的js文件, 以babel-loader进行打包
      exclude: /node_modules/, // 因为node_modules 下的js文件都是第三方模块,已经做好了处理
      loader: "babel-loader",
      options:{
        "presets": ["@babel/preset-env"]
      }
    }

到这里我们再次执行:npx webpack

注意:

由于此处学习的版本和老师讲解存在差异。上述操作后出现大面积报错。原因是babel的配置出现了问题。 具体详见此处: webpack配置babel时候报错ERROR in ./src/main.js Module build failed (from ./node_modules/babel-loade.

步骤:

  • 先卸载babel的安装 cnpm uni babel-core babel-loader babel-plugin-transform-runtime -D
  • 执行新的安装 cnpm i babel-loader @babel/core @babel/runtime @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime -D
  • 安装依赖 cnpm install --save-dev babel-plugin-transform-object-rest-spread
  • 配置.babelrc文件
{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    "@babel/plugin-transform-runtime",
    "@babel/plugin-proposal-class-properties"
  ]
}

接下来再执行 npx webpack 会依次出现 cannot found html-webpack-plugin 和 cannot found clean-webpack-plugin .

猜测可能是新版本的出现,之前的配置方法失效导致,初学也没有更好的办法,因此就执行了: cnpm i html-webpack-plugin clean-webpack-plugin -D

最后 npx webpack 打包终于不报错了,来看一下打包生成的文件:

可以发现,babel已经帮我们将es6的语法转换成es5了。箭头函数也转换成了普通的function(){}. 成功。

美中不足的是,现在我们虽然将语法转换了,但是在一些低版本的浏览器中,promise对象及数组的map函数都是不存在的,他们并不能识别这些内容,所以我们不仅需要 **babel-preset **做语法转换,还需要使用 babelpolyfill 把这些变量补充道低版本的浏览器中。

cnpm i @babel/polyfill -D 安装polyfill 使用polyfill很简单,我们只需要在需要打包的js文件首行 import 即可

可以看到使用polyfill补充语句之后,大暴出来的目标文件变得非常大了。在先前只有10kb不到的大小,现在是700kb。

所以我们需要优化,我们只希望babelpolyfill补充我们在index.js中使用到的es6新语句。 修改webpack.config.js 的配置项:

如果你想给某一项配置参数,那么你需要将其放入一个中括号中,第一个使用的present的名字,第二个参数 放置的是配置参数

文件大小就小了很多了