webpack学习

npm相关

1
npm i webpack --save-dev      开发环境下的依赖

运行cnpm run dev自动打开

1
2
3
4
//package.json文件
"scripts": {
"dev": "webpack-dev-server --open --port 3000 --hot"
}
1
2
3
4
5
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server",
"build": "webpack"
}

webpack功能

  • 代码转换
  • 文件优化
  • 代码分割
  • 模块合并
  • 自动刷新
  • 代码校验
  • 自动发布
loader功能

loader让webpack能够去处理那些非js文件,(webpack自身只能理解js)。loader能让所有类型的文件转换为webpack能够处理的有效模块,然后就可利用webpack的打包能力对其进行处理。

loader的两大目标:

  1. test属性,用于标识出应该被对应的loader进行转换的某个文件。
  2. use属性,表示在转换时,应使用哪个loader。

webpack使用

  • webpack默认只支持js文件
  • node webpack
1
2
3
4
5
//安装本地的webpack
cnpm i webpack webpack-cli -D
//表示开发依赖
//在命令行上输入webpack即可将入口文件打包
//webpack可以进行0配置
手动配置webpack文件

webpack配置文件webpack.config.js

1
2
3
4
5
6
7
8
module.exports = {
mode: 'development',//模式默认两种,production development
entry: path.join(__dirname,'./src/main.js'),
output: {
path: path.resolve(__dirname,'dist'),//路径必须是一个绝对路径
filename: 'bundle.js',//打包后的文件名
}
}

production:开发的未压缩的文件

development:开发的压缩的文件

更改配置文件名字
1
2
3
4
5
在package.json文件中的script对象上
scripts:{
"builder": "webpack --config webpack.configmy.js"
}
//命令行上运行 cnpm run builder即可
webpack以本地形式打开文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cnpm i webpack-dev-server -D
//webpack.config.js
module.exports = {
devServer:{ //开发服务器配置
port: 3000, //在3000端口打开
progress: true, //显示进度条
contentBase: './build', //以当前目录运行程序
compress: true //启动压缩

}
}
//package.json文件
scripts:{
"dev": "webpack-dev-server"
}
webpack解析html模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cnpm i html-webpack-plugin -D
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
output: {
path: path.resove(__dirname,'./dist'),
filename: 'bundle.[hash].js', //每次输出的文件都不同
//filename: 'bundle.[hash:8].js', 只显示8位hash值
}
plugins: [ //数组 放着所有的webpack插件
new HtmlWebpackPlugin({
template: './src/index.html' //以index.html文件为模板
filename: 'index.html', //打包后的文件名
minify: {
removeAttributeQuotes:true, //压缩时,删除属性中的双引号
collapseWhiteSpace: true, //折叠空行,将html文件打包成一行

},
hash: true, //添加哈希戳
})

]
}
webpack解析css模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
cnpm i style-loader css-loader -D  //处理css文件
cnpm i less less-loader -D //less-loader会调用less进行解析
cnpm i node-sass sass-loader -D //处理sass和scss文件
cnpm i stylus stylus-loader -D //处理stylus文件

//main.js文件导入css文件
require('./index.css');

//webpack.config.js
module.exports = {
module: { //模块
rules: [ //规则 css-loader 处理@import这种语法的,将多个css文件合并为一个css文件
//style-loader 他是把css插入到head的标签中
//loader特点:希望一个loader处理一个功能
//只用一个loader用字符串,多个loader用数组,当有参数时可写成对象方式
//loader顺序:默认从右向左执行
{
test: /\.css$/,
use: [
{
loader:'style-loader',
options: {
insertAt: 'top' //将样式插到head顶部
}
},
'css-loader'
]
},
{ test: /\.less$/, use: ['style-loader','css-loader','less-loader'] }
]

}
}
抽离文件的插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cnpm i mini-css-extract-plugin -D
//专门用于抽离css样式的插件

//webpack.config.js
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filenmae: 'main.css' //抽离出的名字叫main.css
})
],
module: {
rules:[
{
test:/\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader' //先解析成css样式,然后使用MiniCssExtractPlugin这个插件的加载器将样式分离成一个main.css文件
]
}
]
}
自动加前缀的插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cnpm i postcss-loader autoprefixer 

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

//新建一个postcss.config.js文件
module.exports = {
plugins: [require('autoprefixer')]
}
webpack中处理js模块
  • 将es6转为es5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
cnpm i babel-loader @babel/core @babel/preset-env -D
// @babel/core是babel的核心模块,@babel/preset-env 将高级语法转为低级语法
cnpm i eslint eslint-loader -D //语法检查

cnpm i @babel/plugin-proposal-class-properties -D //像class类这些高级语法

cnpm i @babel/runtime @babel/plugin-transform-runtime -D @babel/runtime //像generator遍历器,在plugins中配置

//webpack.config.js
module.exports = {
module: {
noParse: /jquery/, //不去解析jquery中的依赖库
rules: [
{ test: /\.js$/,
use: {
{
loader:'babel-loader', //用该模块将es6转为es5
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties','@babel/plugin-transform-runtime']
},
{
loader: 'eslint-loader', //校验es语法规范
options: {
enforce: 'pre' //强制先执行,因为loader默认是从下往上执行
}
}
}
},
exclude: /node_modules/, //不去node_module中找
include: path.resolve(__dirname,'src') //去src文件夹下找
}
]
}
}
webpack第三方模块的使用
1
2
3
4
5
6
7
8
module.exports = {
resolve: { //解析第三方包 common
modules: [path.resolve('node_modules')], //解析时先到该目录下寻找
alias:{ //别名 vue vue.runtime
bootstrap: 'bootstrap/dist/css/bootstrap.css'
}
}
}
1
2
3
4
5
6
7
8
9
10
cnpm i jquery 
cnpm i expose-loader

//main.js文件
import $ from 'jquery'; //此$不是window上的的$
window.$ //undefined

import $ from 'expose-loader?$!jquery';
//expose-loader 暴露 全局的loader 内联的loader
//pre 前面执行的loader normal 普通loader 内联loader 后置 postloader

将$暴露给window的另一种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cnpm i jquery 
cnpm i expose-loader

//main.js文件
import $ from 'jquery';

//webpack.config.js文件
module: {
rules: [
{
test:require.resolve('jquery'),
use: 'expose-loader?$!jquery'
}
]
}

在每个模块中注入$对象

1
2
3
4
5
6
7
8
9
10
11
12
13
//webpack.config.js文件
const webpack = require('webpack')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
new Webpack.ProvidePlugin({ //在每个模块中注入$
$: 'jquery'
})
]
}
1
2
3
4
5
6
7
8
9
//main.js文件
import $ from 'jquery' //运行npm run build时会将jquery打包

//不像将jquery打包,只需在webpack.config.js文件中
module.exports = {
externals: {
jquery: '$'
}
}
webpack打包图片
  1. 在js中创建图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cnpm i file-loader -D  
//file-loader默认会在内部生成一张图片到dist文件夹下,并把生成的图片的名字返回回来

//main.js文件
import logo from './logo.png';
//把图片引入,返回结果是一个新的图片地址
let image = new Image();
image.src = logo; //就是一个普通的字符串
document.body.appendChildren(image)

//webpack.config.js文件
module.exports = {
module: {
rules: [
{ test: /\.(png|jpg|gif)$/, use: 'fle-loader' },
]
}
}
  1. 在css引入background(‘url’)

  2. 使用标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    cnpm i html-withimg-loader -D
    cnpm i url-loader -D
    //在html文件中引入image,但是路径不在打包目录中的路径中
    module.exports = {
    module: {
    rules: [
    {test: /\.html$/, use: 'html-withimg-loader'},
    {
    test: /\.(png|jpg|gif)$/,
    //做一个限制,当图片小于多少k时用base64来转化
    //否则用file-loader产生真实的图片
    use: {
    loader: 'url-loader',
    options: {
    limit: 200*1024,
    outputPath: '/img/' //输出路径放置在img目录下
    publicPath: 'http://www.chenchuyin.com' //统一给图片加上该域名
    }
    }
    }
    ]
    }
    }
webpack有多入口文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cnpm i html-webpack-plugin - D
cnpm i @babel/core @babel/preset-dev @babel-loadedr @webpack-dev-server -D
//webpack.config.js文件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//源码映射 会单独生成一个sourcemap文件,出错了胡hi标识当前出错的列和行
devtool: 'source-map', //增加映射文件 可以帮我们调试源代码
//多入口
entry: {
home: './src/index.js',
other: './src/other.js'
},
output: {
//[name]表示 home 和 other [hash]添加哈希戳
filename: '[name].[hash].js',
path: path.resolve(__dirname,'dist')
},
plugins: [
//若无chunks,会把两个js文件都引入到index.html和other.html文件中
new HtmlWebpackPlugin({
tempalte: './index.html',
filename: 'index.html',
chunks: ['index'] //index.html文件引入index.js文件
}),
new HtmlWebpackPlugin({
tempalte: './other.html',
filename: 'other.html',
chunks: ['other','index'] //other.html文件引入other.js和index.js文件
})
]
}
  1. 不会产生单独文件,但是可以显示行和列

    1
    devtol:'eval-source-map'
  2. 不会产生列,但是是一个单独的映射文件

    1
    devtoole: 'cheap-module-source-map'  //产生后你可以保存起来
  3. 不会产生文件,集成在打包后的文件中,不会产生列

    1
    devtoole: 'cheap-module-eval-source-map'
自动打包出实体文件
1
2
3
4
5
6
7
8
module.exports = {
watch: true, //监听文件,只要文件有所修改便直接生成新的实体文件
watchOptions:{ //监控选项
poll: 1000, //每秒 查看文件是否更改 1000次
aggreatement: 500, //防抖 如果一直输入代码,则每过500次再生成实体文件
ignored: /node_modules/ //不需要进行监控的文件
}
}
webpack小插件
  1. cleanWebpackPlugin
  2. copyWebpackPlugin
  3. bannerPlugin(内置的,不需要安装该模块)
1
2
3
4
5
6
7
8
9
10
11
12
13
cnpm i clean-webpack-plugin copy-webpack-plugin webpack -D

const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
plugins: [
new CleanWebpackPlugin('./dist'), //打包之前先将dist目录清除
new CopyWebpackPlugin(
{from: 'doc', to: './'} //将doc.txt文件复制到输出的根目录下
),
new webpack.BannerPlugin('make 2019 by chenchuyin')
//会将这句话插入到出口js文件的开头部分
]
webpack实现跨域功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//webpack-dev-server默认打开localhost:8080,若想访问3000端口,可使用代理
module.exports = {
devServer:{
proxy: { //重写的方式 把请求代理到express服务器上
// '/api': 'http://localhost:3000', //配置了一个代理
'api': {
target: 'http://localhost:3000',
pathRewrute: {'api': ''}
}
}
}
}

//server.js文件
const express = require('express')
let app = express();
app.get('/user', (req,res) => {
res.json({name:'陈楚吟'})
})
app.listen(3000);

//index.js文件
let xhr = new XMLHttpRequest();
xhr.open('GET','/api/user',true); //请求还是有api目录,显示时将其删除
xhr.onload = function(){
console.log(xhr.response);
}
xhr.send();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
devServer:{
//我们前端只想单纯模拟数据
before(app){
app.get('/user', (req,res) => {
res.json({name: '陈楚吟'})
})
}
}

//server.js文件
const express = require('express')
let app = express();
app.listen(3000);

//index.js文件
let xhr = new XMLHttpRequest();
xhr.open('GET','/api/user',true); //请求还是有api目录,显示时将其删除
xhr.onload = function(){
console.log(xhr.response);
}
xhr.send();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cnpm i webpack-dev-middleware -D

devServer:{
//有服务端,不用代理来处理,在服务端中启动webpack端口用服务端端口(即前端页面和服务器在同一个端口)

}

//server.js文件
const express = require('express');
const webpack = require('webpack');
let app = express();

//中间件
let middle = require('webpack-dev-middleware');
let config = require('./webpack.config.js');
let compiler = webpack(config);
app.use(middle(compiler));
app.get('/user', (req,res) => {
res.json({name:'陈楚吟'})
})
app.listen(3000);
实现多线程打包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
cnpm i happypack
//模块happypack可以实现多线程来打包进程
let Happypack = require('happypack')
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
include: 'src',
use: 'Happypack/loader?id=js'
},
{
test: /\.css$/,
use: 'Happypack/loader?id=css'
}
]
},
palugins: [
new Happypack({
id: 'css',
use: ['style-loader','css-loader']
})
new Happypack({
id: 'js',
use: [{
loader:'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react'
]
}
}]
})
]
webpack自带优化
1
2
3
4
//import 在生成环境下会自动去除没用的代码
//因tree-shaking方法会将没用到的代码自动删除掉·

//require(es6)模块会把结果都放到default上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = { 
optimization: { //将公共部分抽离出来
splitChunks: { //分割代码块
cacheGrounps:{ //缓存组
common:{ //公共的模块
chunks: 'initial', //代码在刚开始时就被抽离出
minSize: 0, //代码有多少字节才被抽离
minChunks: 2 //代码块被使用多少次才被抽离
},
vendor:{
priority: 1, //抽离权重,较大的先抽离
test: /node_modules/, //将第三方模块抽离出来
chunks: 'initial',
minSize: 0,
minChunks: 2
}
}
}
}
}
webpack懒加载
  • vue和react的懒加载其实都是通过import实现
1
2
3
4
5
6
7
8
9
10
//index.js
let button = document.createElement('button')
button.innerHTML = 'hello';
button.addEventListener('click',function(){
//import 是草案中的语法,由jsonp实现动态加载文件
//返回值是一个1promise对象
import('./source.js').then(data => {
console.log(data.default);
})
})
webpack热更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
devServer: {
hot: true, //启动热更新操作
port: 3000
},
plugins: [
new webpack.NameModulesPlugin(), //告诉我们哪个模块热更新
new webpack.HotModuleReplacementPlugin() //热更新插件
]
}

//index.js文件
import str from './source';
if(module.hot){
module.hot.accept('./source', () => {
require('./source');
})
}
Tapable
  • webpack本质上是一种事件流机制,它的工作流程是将各个插件串联起来,而实现这一切的核心是Tapable,Tapable是有点类似于nodeJs的events库,核心原理也是依赖于发布订阅模式。
1
2


webpack中使用vue

在普通网页中使用vue

1
2
3
4
5
6
7
8
9
10
//1.使用script标签,引入vue的包   (此包功能较全)
<scrpit src="./lib/vue.js"></srcipt>

//2.在index页面中,创建一个id为app的div容器
<div id="app"></div>

//3.通过new Vue得到一个vm实例
let vm = new Vue({
el: '#app'
})

在webpack中尝试使用vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//注意:在webpack中,使用import Vue from 'vue' 导入的Vue构造函数功能不完整,只提供了runtime-only的形式,并无提供像网页中那样的使用方式;
import Vue from 'vue'

let vm = new Vue({
el: '#app'
})

解决:指定完整vue.js文件的路径
方式一:import Vue from './node_modules/vue/dist/vue.js';
方式二:import vue from 'vue'
在webpack.config.js文件中
module.exports = {
resolve: {
alias: { //修改vue时被导入时包的路径
"vue$": "vue/dist/vue.js"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cnpm i vue -S
cnpm i vue-loader vue-template-compiler -D

//webpack.config.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')

const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: "development",
entry: './src/index.js',
output: {
path: path.resolve(__dirname,'./dist'),
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
new VueLoaderPlugin()
],
module: {
rules: [
{ test: /\.css/, use: ['style-loader','css-loader'] },
{ test: /\.vue/, use: 'vue-loader' }
]
}
}
1
2
3
4
5
6
7
1.安装 vue 包,cnpm i vue -S
2.由于在webpack中推荐使用vue组件模板文件定义组件,故需要安装能解析这种文件的loader cnpm i vue-loader vue-template-compiler -D
3.在main.js中导入vue模块, import Vue from 'vue'
4.定义.vue结尾的组件,由三部分组成:template script style
5.使用import login from './login.vue'导入这个组件
6.创建vm实例 let vm = new Vue({ el: '#app', render: c => c(login) })
7.在页面创建一个id为app的div元素,作为我们vm实例要控制的区域;

注意:App这个组件是通过vm实例的render函数渲染出来的,render函数如果要渲染组件,渲染出来的组件只能放到el:‘#app’ 所指定的元素中。

Account和Goodlist组件是通过路由配置监听到的,所以这两个组件只能展示到属于路由的中去。

包的查找规则

找vue文件

  1. 找项目根目录中有没有 node_modules 的文件夹
  2. 在node_modules中根据包名找对应的vue文件夹
  3. 在vue文件夹中,找一个叫package.json 的包配置文件
  4. 在 package.json 文件中,查找一个main 属性【main属性指定了这个包在被加载时的入口文件】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//sf-pack.js文件
//需要找到当前执行命令的路径,拿到webpack.config.json
const path = require('path')

//config配置文件
let config = require(path.resolve('webpack.config.js'));

let Compiler = require('../lib/Compiler.js');
let compiler = new Compiler(config);
//标识运行编译
compiler.run();

//Compiler.js文件
const path = require('path');
const fs = require('fs');
class Compiler{
constructor(config){
//entry output
this.config = config;
//需要保存入口文件的路径
this.entryId; //'./src/index.js'
//需要保存所有模块依赖
this.modules = {};
this.entry = config.entry; //入口路径
//工作路径
this.root = process.cwd();
}
getSource(modulePath){
let content = fs.readFileSync(modulePath,'utf-8');
return content;
}
//构建模块
buildModule(modulePath,isEntry){
//拿到模块内容
let source = this.getSource(modulePath);
//模块id modulePath = modulePath - this.root;
let modulePath = './' + path.relative(this.root,modulePath);
}
run(){
//执行并且创建模块的依赖关系
this.buildModule(path.resolve(this.root,this.entry),true);
//true表示为主模块
//发射一个文件,该文件即为打包后的文件
this.emitFile();
}
}
module.exports = Compiler
文章目录
  1. 1. npm相关
  2. 2. 运行cnpm run dev自动打开
  3. 3. webpack功能
    1. 3.0.1. loader功能
  • 4. webpack使用
    1. 4.0.1. 手动配置webpack文件
    2. 4.0.2. 更改配置文件名字
    3. 4.0.3. webpack以本地形式打开文件
    4. 4.0.4. webpack解析html模块
    5. 4.0.5. webpack解析css模块
    6. 4.0.6. 抽离文件的插件
    7. 4.0.7. 自动加前缀的插件
    8. 4.0.8. webpack中处理js模块
    9. 4.0.9. webpack第三方模块的使用
    10. 4.0.10. webpack打包图片
    11. 4.0.11. webpack有多入口文件
    12. 4.0.12. 自动打包出实体文件
    13. 4.0.13. webpack小插件
    14. 4.0.14. webpack实现跨域功能
    15. 4.0.15. 实现多线程打包
    16. 4.0.16. webpack自带优化
    17. 4.0.17. webpack懒加载
    18. 4.0.18. webpack热更新
    19. 4.0.19. Tapable
    20. 4.0.20. webpack中使用vue
    21. 4.0.21. 包的查找规则
  • |