关于webpack的一些问题

1.详解webpack中的hash、chunkhash、contenthash区别

hash一般结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的MD5值。如果文件内容改变,那么对应文件hash值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,今儿更新本地缓存。实际使用时,几种hash计算的区别。我们先建一个测试案例来模拟下:
项目结构很简单,入口文件index.js,引用了index.css。然后新建了jquery.js和test.js作为公共库。

//index.js
 require('./index.css')
 module.exports = function(){
  console.log(`I'm jack`)
  var a = 12
 }
 
//index.css
 .selected : {
   display: flex;
   transition: all .6s;
   user-select: none;
   background: linear-gradient(to bottom, white, black);
 }
复制代码

接着我们修改webpack.config.js来模拟不同hash计算

(1)hash

hash是跟整个项目的构建有关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值

var extractTextPlugin = require('extract-text-webpack-plugin'),
 path = require('path')
 
module.exports = {
 context : path.join(__dirname,'src'),
 entry:{
 main: './index.js',
 vender:['./jquery.js','./test.js']
 },
 module:{
 rules:[{
  test:/\.css$/,
  use: extractTextPlugin.extract({
  fallback:'style-loader',
  use:'css-loader'
  })
 }]
 },
 output:{
 path:path.join(__dirname, '/dist/js'),
 filename: 'bundle.[name].[hash].js',
 },
 plugins:[
 new extractTextPlugin('../css/bundle.[name].[hash].css')
 ]
}
复制代码

根据上面的配置,我们执行webpack命令之后,可以得到下面的结果

image.png

image.png
可以看到构建生成的文件hash值都是一样的,所以hash计算是跟整个项目构建相关,同一次构建构成中生成的哈希都是一样的

(2)chunkhash

采用hash计算的话,每一次构建后生成的哈希值都不一样,即使文件内容压根没有改变。这样子是没办法实现缓存效果,我们需要换另一种哈希值计算方式,即chunkhash
chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。我们在生产环境里把一些公共库和程序入口文件分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不受影响。

var extractTextPlugin = require('extract-text-webpack-plugin'),
 path = require('path')
 
module.exports = {
 ...
 ...
 output:{
 path:path.join(__dirname, '/dist/js'),
 filename: 'bundle.[name].[chunkhash].js',
 },
 plugins:[
 new extractTextPlugin('../css/bundle.[name].[chunkhash].css')
 ]
}
复制代码

采用chunkhash计算的执行结果

image.png
image.png
可以看到由于采用chunkhash,所以项目主入口文件的index.js及其对应的依赖文件index.css由于被打包在同一个模块,所以用相同的chunkhash,但是公共库由于是不同的模块,所以有单独的chunkhash.这样子就保证了在线上构建的时候只要文件内容没有更改就不会重复构建。

(3)contenthash

在chunkhash的例子,我们可以看到由于index.css被index.js引用了,所以共用相同的chunkhash值。但是这样子有个问题,如果index.js更改了代码,css文件就算内容没有任何改变,由于是该模块发生了改变,导致css文件会重复构建。
这个时候,我们可以使用extra-text-webpack-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建。

var extractTextPlugin = require('extract-text-webpack-plugin'),
 path = require('path')
 
module.exports = {
 ...
 ...
 output:{
 path:path.join(__dirname, '/dist/js'),
 filename: 'bundle.[name].[chunkhash].js',
 },
 plugins:[
 new extractTextPlugin('../css/bundle.[name].[contenthash].css')
 ]
}
复制代码

采用contenthash计算的执行结果

image.png
image.png

2.webpack 的 loader 和 plugin

从功能角度看

(1)loader–加载
webpack本身只能打包commonjs规范的js文件,所以针对css、图片等格式的文件没办法打包,就需引入第三方模块进行打包。
loaderloader虽然是扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译。loader是运行在NodeJS中,仅仅只是为了打包。

如:css-loader和style-loader模块是为了打包css的、
babel-loader和babel-core模块时为了把ES6的代码转成ES5、
url-loader和file-loader是把图片进行打包的。

(2)plugin– plugin完成的是loader不能完成的功能
plugin也是为了扩展webpack的功能,但是 plugin 是作用于webpack本身上的。而且plugin不仅只局限在打包,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块,避免首屏加载的bundle文件,或者按需加载的bundle文件体积过大,导致加载时间过长,是一把优化的利器。而在多页面应用中,更是能够为每个页面间的应用程序共享代码创建bundle。

插件可以携带参数,所以在plugins属性传入new实例。
如:针对html文件打包和拷贝(还有很多设置)的插件:html-webpack-plugin
不但完成了html文件的拷贝,打包,还给html中自动增加了引入打包后的js文件的代码(<script src=""></script>),还能指明把js文件引入到html文件的底部等等。

plugins:[   
        //对html模板进行处理,生成对应的html,引入需要的资源模块
        new HtmlWebpackPlugin({
            template:'./index.html',//模板文件,即需要打包和拷贝到build目录下的html文件
            filename:'index.html',//目标html文件
            chunks:['useperson'],//对应加载的资源,即html文件需要引入的js模块
            inject:true//资源加入到底部,把模块引入到html文件的底部
        })
]
复制代码

从运行时机的角度区分

1 . loader运行在打包文件之前(loader为在模块加载时的预处理文件)
2. plugins在整个编译周期都起作用。


【Loader】:用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在build中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL。比如说:CSS-Loader,Style-Loader等。

loader的使用很简单:

在webpack.config.js中指定loader。module.rules可以指定多个loader,对项目中的各个loader有个全局概览。

loader是运行在NodeJS中,可以用options对象进行配置。plugin可以为loader带来更多特性,让loader可以进行压缩,打包,语言翻译等等。

loader从模板路径解析,npm install node_modules。也可以自定义loader,命名XXX-loader。

语言类的处理器loader:CoffeeScript,TypeScript,ESNext(Bable),Sass,Less,Stylus。任何开发技术栈都可以使用webpack。

【Plugin】:目的在于解决loader无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块,避免首屏加载的bundle文件,或者按需加载的bundle文件体积过大,导致加载时间过长,是一把优化的利器。而在多页面应用中,更是能够为每个页面间的应用程序共享代码创建bundle。

webpack功能强大,难点在于它的配置文件,webpack4默认不需要配置文件,可以通过mode选项为webpack指定了一些默认的配置,mode分为:development/production,默认是production。

插件可以携带参数,所以在plugins属性传入new实例。

【Mode】可以在config文件里面配置,也可以在CLI参数中配置:webpack –mode=production(一般会选择在CLI,也就是npm scripts里面进行配置)。

在webpack4以下版本,webpack3.XX,通过plugins进行环境变量的配置。

【resolve】模块,resolver是个库,帮助webpack找到bundle需要引入的模块代码,打包时,webpack使用enhanced-resolve来解析路径。

 resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  }
复制代码

【Manifest】管理所有模块之间的交互。runtime将能够查询模块标识符,检索出背后对应的模块。

问题1:webpack通过使用bundle计算content hash作为文件名称,文件修改,新的content hash执向新的文件,缓存无效,但是文件内容没有修改,计算的hash还是会改变,因为,runtime和manifest的注入在每次构建都会发生变化。要想解决这个文件可以了解更多的runtime和manifest。

webpack原理:从配置文件定义的模块列表开始,处理应用程序,从入口文件开始递归构建一个依赖图,然后将所有模块打包为少量的bundle,通常只有一个,可由浏览器加载。


3.webpack打包(主要是处理html文件),并启动服务器

如何在webpack打包过程中,处理html文件。项目目录结构
image.png
现在需要,通过webpack打包后,把所有的打包结果放在项目目录下的build目录下,直接启动webpack-dev-server进行运行。

一、安装插件:

1、ExtractTextPlugin: npm i extract-text-webpack-plugin –save-dev

2、html-webpack-plugin:npm i html-webpack-plugin –save-dev

二、在webpack.config.js中增加如下代码:

const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
 
module.exports = {
    
    …………………………省略部分配置…………………………
 
	plugins:[   
		//对html模板进行处理,生成对应的html,引入需要的资源模块
		new HtmlWebpackPlugin({
			template:'./index.html',//模板文件,即需要打包和拷贝到build目录下的html文件
			filename:'index.html',//目标html文件
			chunks:['useperson'],//对应加载的资源,即html文件需要引入的js模块
			inject:true//资源加入到底部,把模块引入到html文件的底部
		})
  ]
}
复制代码

三、最终的webpack.config.js的代码

const path = require('path');
const webpack = require('webpack')
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
 
module.exports = {
	mode:"development",
	//入口文件(要打包的文件)
	entry:{
		"useperson":"./useperson.js"
	},
 
	//出口文件(打包的结果)
	output:{
		"path":__dirname+"/build/",
		"filename":"[name].bundle.js"
	},
	//启动服务
	devServer:{
		host:"localhost",
		port:"8080",
		contentBase:__dirname+"/build"
	},
	module: {
        //加载器配置
        rules: [
            { 
				test: /\.js$/, 
				loader: 'babel-loader',
				exclude: /node_modules/, //排除掉node_modules下的js文件,即不解析这个文件夹下的js文件
				query: {
				 presets: ['latest'] //按照最新的ES6语法规则去转换,可以把这个写在预设(.babelrc)文件里
				}
			},
			
        ]
    },
	plugins:[   
		//对html模板进行处理,生成对应的html,引入需要的资源模块
		new HtmlWebpackPlugin({
			template:'./index.html',//模板文件,即需要打包和拷贝到build目录下的html文件
			filename:'index.html',//目标html文件
			chunks:['useperson'],//对应加载的资源,html文件需要引入的js模块
			inject:true//资源加入到底部,把模块引入到html文件的底部
		})
  ]
}
复制代码

四、执行webpack命令

五、执行webpack-dev-server启动服务器。

六、你也可以把webpack和webpack-dev-server一次性执行。需要在package.json里写上如下代码(npm script):

"scripts": {
    "dev":"webpack && webpack-dev-server",
    …………………………………………
},
复制代码

此时,直接在命令行输入:npm run dev 就可以把webpack和webpack-dev-server两个命令一次性执行了。

4.webpack 处理 image 是用哪个 loader,限制成 image 大小的是

loaders: [
            { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' },
        ]
复制代码
{
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
复制代码

test 属性代表可以匹配的图片类型,以竖线隔开即开。
limit 字段代表图片打包限制,这个限制并不是说超过了就不能打包,而是指当图片大小小于限制时会自动转成 base64 码引用。如大于10000字节的图片正常打包,小于10000字节的图片以 base64 的方式引用。
通过 name 字段来指定图片打包的目录与文件名。

5.webpack 将 css 合并成一个

webpack把各个模块的css打包成一个方法:
webpack.config.js配置如下
var ExtractTextPlugin = require(“extract-text-webpack-plugin”); //extract-text-webpack-plugin安装此插件

module:{
    loaders:[
            {
                test: /\.css$/, 
                loader: ExtractTextPlugin.extract("style-loader", "css-loader")
            }
    ]
},
plugins:[
    new ExtractTextPlugin("css/[name].css")//则会生成一个css文件
]
复制代码

比如你的js文件中这样引入即可:
import ‘./css/lib/bootstrap.min.css’
import ‘./css/test.css’;

6.webpack 的摇树(tree-shaking)对 commonjs 和 es6 module 都生效么,原理是


一个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打包到bundle中去,tree shaking 就是只把用到的的方法打入 bundle,没用到的方法会在 uglify 阶段被擦除掉。

代码不会被执行,不可到达
代码执行的结果不会被用到
代码只会影响死变量(只写不读)

使用

webpack4 默认支持,在 .babelrc 里面设置 modules: false 即可
production mode 的情况下默认开启

要求

必须是ES6语法,CJS的方式不支持

原理

利用ES6模块的特点:
只能作为模块顶层的语句出现;
import 的模块名只能是字符串常量
import 的常量是不可变的

uglify 阶段删除无用代码


摇树处理 Tree Shaking 是一项在消除 Javascript 上下文中死代码的技术。 它依赖于ES2015模块语法的静态结构,即 import 和 export。这个专业名词被 ES2015 module bundler rollup 所汇总推广了。

webpack 2版本自带内置支持ES2015模块(alias harmony modules 别名和声模块)以及未使用的模块导出检测。

新的webpack 4版本扩展了这一功能,通过 package.json 的 sideEffects 属性向编译器提供提示,表示项目中的哪些文件是“pure 纯粹的”,因此如果未使用则可以安全修剪。

webpack 里的tree-shaking的到来不得不归功于es6规范的模块。为什么这么说,如今的前端模块规范很多,比较出流行的比如commonJS , AMD , es6 ,我简单的说一下commonJS和es6模块的区别。

commonJS 模块

commonJS的模块规范在Node中发扬光大,总的来说,它的特性有这几个:

1.动态加载模块
commonJS和es6的最大区别大概就在于此了吧,commonJS模块的动态加载能够很轻松的实现懒加载,优化用户体验。

2.加载整个模块
commonJS模块中,导出的是整个模块。

3.每个模块皆为对象
commonJS模块都被视作一个对象。

4.值拷贝
commonJS的模块输出和 函数的值传递相似,都是值的拷贝

es6 模块

1.静态解析
即在解析阶段就确定输出的模块,所以es6模块的import一般写在被引入文件的开头。

2.模块不是对象
在es6里,每个模块并不会当做一个对象看待

3.加载的不是整个模块
在es6模块中经常会看见一个模块中有好几个export 导出

4.模块的引用
es6模块中,导出的并不是模块的值拷贝,而是这个模块的引用


在结合es6模块和commonJS模块的区别之后,我们知道es6的特点是静态解析,而commonJS模块的特点是动态解析的,因此,借于es6模块的静态解析,tree-shaking的实现才能成为可能。
在webpack中,tree-shaking指的就是按需加载,即没有被引用的模块不会被打包进来,减少我们的包大小,缩小应用的加载时间,呈现给用户更佳的体验。


如何使用tree-shaking–webpack默认es6规范编写的模块都能使用tree-shaking。

见下面文章:[blog.csdn.net/haodawang/a…]

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享