构建就是把源代码转换成发布到线上的可执行JavaScrip、CSS、HTML代码,包括如下内容:
首先确保电脑已经安装了nodejs,推荐采用nvm的形式进行安装,这样就不用配置环境变量或者创建软链接。
mkdir webpack-learn //通过命令行创建文件夹
cd webpack-learn //打开创建的文件夹
npm init -y //初始化一个项目
npm install webpack webpack-cli -D //本地安装webpack和webpack-cli
mkdir src //创建src目录来存放源代码
mkdir dist //创建dist目录来存放打包后的代码
复制代码
在src目录下创建index.js
let str = require('./a');
console.log(str);
复制代码
在src目录下创建a.js
module.exports='webpack-learn'
复制代码
在dist目录下创建index.html文件
Document
复制代码
利用webpack进行打包,关于commonjs不清楚的,请参考require()源码解读
npx webpack -- mode development //以默认以开发模式进行打包
复制代码
从上面的代码中可以看到文件打包到了dist/main.js中,所以需要进行相应的配置webpack.config.js来自定义结果文件名。
const path=require('path');
module.exports={
entry: './src/index.js', //入口
output: {
path: path.resolve(__dirname,'dist'), //出口,绝对路径
filename:'bundle.js'
},
module: {}, //配置loader
plugins: [], //配置插件
devServer: {} //配置本地服务
resolve:{}, //配置解析文件路径等
}
复制代码
//index.js
console.log('hello');
复制代码
//a.js
console.log('world')
复制代码
const path=require('path');
module.exports={
//index和a之间没有依赖关系,只是单纯的合并
entry: ['./src/index.js','./src/a.js'],
output: {
path: path.resolve(__dirname,'dist'),
filename:'bundle.js'
},
module: {},
plugins: [],
devServer: {}
resolve:{},
}
复制代码
//a.js
let str =require('./c')
console.log(str);
复制代码
//b.js
let str =require('./d')
console.log(str);
复制代码
//c.js
module.export = 'hello'
复制代码
//d.js
module.export = 'world'
复制代码
const path=require('path');
module.exports={
//多入口拆分功能,可以两个页面分别引用一个,也可以一个页面引用多个
//配合后面的html-webpack-plugin使用
entry: {
pageA:'./src/a',
pageB:'./src/b'
},
output: {
path: path.resolve(__dirname,'dist'),
//带有哈希值的文件名exp:pageA.fa112c62、pageB.fa112c62
filename:'[name].[hash:8].js'
},
module: {},
plugins: [],
devServer: {}
resolve:{},
}
复制代码
现在页面是手动创建到dist目录下的,一个页面还好,如果存在多个页面,手动创造html的代价是很大的,可以利用html-webpack-plugin来自动创建页面:
npm install html-webpack-plugin -D
复制代码
//src/index.html
Document
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
pageA:'./src/a',
pageB:'./src/b'
},
output: {
path: path.resolve(__dirname,'dist'),
filename:'[name].[hash:8].js'
},
module: {},
plugins: [
//在实际项目中,通过读取需要创建的页面信息,遍历创建实例
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'pageA.html',
chunks:['pageA,pageB'], //数组,可以放多个chunk
//页面资源携带哈希值exp:pageA.fa112c62?r2452567&4124
//中间哈希一直都有,这个后面的哈希只在页面引用添加在页面中
hash:true,
minify:{
collapseWhitespace:true, //压缩代码,去除空格和换行
removeAttributeQuotes:true//压缩代码,去除属性双引号
}
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'pageB.html',
chunks:['pageB'],
hash:true,
minify:{
collapseWhitespace:true,
removeAttributeQuotes:true
}
})
],
devServer: {},
resolve:{},
}
复制代码
由于每次打包都需要执行npx webpack --mode development,所以可以在package.json中进行配置:
"scripts": {
//等价于webpack --config webpack.config.js --mode development
//默认是执行webpack.config.js,可以根据env来配置执行不同的文件
"start": "webpack --mode development"
},
复制代码
首先必须的安装插件webpack-dev-derver:
npm install webpack-dev-server -D
复制代码
修改script:
"scripts": {
"dev":"webpack-dev-server --mode development"
"build": "webpack --mode development"
},
复制代码
修改配置文件:
//index.html
Document
复制代码
//a.js
module.exports = 'hello'
复制代码
//index.js
let str = require('./a');
document.getElementById('app').innerHTML = str;
复制代码
const path=require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports={
entry: './src/index.js',
output: {
path: path.resolve(__dirname,'dist'),
filename:'bundle.js'
},
module: {},
plugins: [
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
hash:true,
minify:{
collapseWhitespace:true,
removeAttributeQuotes:true
}
})
],
devServer: {
//devServer会在打包的时候把生成的文件放在内存中,
//并且是以根目录为参照目录而不是dist目录,所以需要修改
contentBase:'./dist',
port:'3000',
}
resolve:{},
}
复制代码
通过浏览器,输入localhost:3000就可以看到
通过修改a.js中module.exports = 'hello1',可以看到
这样会有一个问题,就是只要源代码中存在改动,就会刷新页面。假如在react本地开发的时候,有很多组件,其中一个组件的代码修改了,不希望所有的组件都更新,可以利用热更新来解决:
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports={
entry:'./src/index.js',
output: {
filename:'bundle.js',
path: path.resolve(__dirname,'dist')
},
module: {},
plugins: [
//使用热更新插件
new webpack.HotModuleReplacementPlugin();
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
hash:true,
minify:{
collapseWhitespace:true,
removeAttributeQuotes:true
}
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true //热更新开关,使用websocket来通知页面更新
},
resolve:{},
}
复制代码
//index.js
let str = require('./a');
document.getElementById('app').innerHTML = str;
//这里必须加这段,不然的话,还是没有办法使用热更新
if(module.hot){
module.hot.accept();
}
复制代码
webpack每找到一个Module,就会根据配置的Loader去找出对应的转换规则,让js能过编译css、ejs、jsx和图片的各种格式。
//index.css
body{
background:red;
border-radio:4px;
}
复制代码
//一般前端代码使用import引入模块,node服务使用require引入模块
import str from './a'
import './index.css'
document.getElementById('app').innerHTML=str
if(module.hot){
module.hot.accept();
}
复制代码
执行npm run build之后发现出现以下报错,说明需要配置loader:
首先需要安装loader
npm install css-loader style-loader -D
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
//多个loaders用数组,loader的形式有字符串和对象两种
//字符串形式:'xxx?option1!yyyy'
//对象形式{loader:'xxx',options:{option1:yyyy}}
//less-laoder将less转化为css,css-loader解析css,style-loader插入到style标签中
use:['style-oader','css-loader']
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
用npm run dev启动服务可以看到效果,但是这样有一个问题,css样式采用的内嵌式,最好能抽离出来使用外链式引入,可以使用插件mini-css-extract-plugin:
npm install mini-css-extract-plugin -D
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
options:{
//将css中的路经前面添加,background:url('xxxx')
//http://ssss/xxxxx
publicPath:'http://sssss'
}
},
'css-loader'
]
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
由于前端写的代码要兼容各种浏览器,css属性为了兼容各大浏览器,往往需要添加各种前缀,但是同一个属性写多份,这个工作量还是比较大的,有一个postcss-loader可以配合autoprefixer插件使用
//index.css
body{
background:red;
transform: rotate(0,0,100deg);
}
复制代码
npm install postcss-loader autoprefixer -D
复制代码
//创建postcss.config.js
module.exports ={
plugins:[
require('autoprefixer')
]
}
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[
{loader:MiniCssExtractPlugin.loader},
'css-loader',
'postcss-loader' //添加css前缀处理laoder
]
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
项目中引入图片的方式有三种:
//index.js
import './index.css'
import jpg from './1.jpg'
let img = new Image();
img.src=jpg;
document.body.appendChild(img);
if(module.hot){
module.hot.accept();
}
复制代码
//index.css
body{
background: url('./1.jpg') no-repeat right;
}
复制代码
//index.html
Document
复制代码
处理前两种引入图片的方式需要使用file-loader和url-loader,其中url-laoder内部会引用file-loader,它们的作用就是解析js和css中的图片链接然后将图片变成base64。后一种引入图片的方式需要使用html-withimg-loader。
npm install file-loader url-loader html-withimg-loader -D
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
//大于8k的图片会打包到dist目录下,小于8k的图片会生成base64插入到引用的地方
//base64会使资源变大1/4,但是base64无需发送请求,资源比较小时使用base64最佳
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
由于ES6的简洁和API扩展,有很多开发者使用ES6进行开发,但是由于浏览器的品牌和版本的不同,就出现了开发时使用ES6然后同意转化成ES5的情况。这时候就需要使用babel对ES6+的语法进行转译,关于babel的各种配置可以参考对babel-transform-runtime,babel-polyfill的一些理解。
npm install babel-core babel-loader babel-preset-env babel-preset-stage-0 babel-plugin-tranform-runtime -D
复制代码
//创建.babelrc文件
//preset中包含了一组用来转换ES6+的语法的插件,但是还不转换新的API
//如需使用新的API,例如set(),还需要使用对应的转换插件或者polyfill(填充库)
{
presets:{
'env', //环境变量,根据不同浏览器环境而对应的转码
'stage-0' //转译ES6+(0 > 1 > 2 > 3 > 4)
}
plugins:{
'tranform-runtime'
}
}
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
},
{ //使用babel-loader
test:/\.js$/,
use:'babel-loader',
exclude:/node_modules/ //排除编译ndoe——modules
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
resolve:{
extensions:['.js','.css','.vue']
},
复制代码
resolve:{
extensions:['.js','.css','.vue'],
alias:{
bootstrap:'bootstrap/dist/css/bootstrap.css' //import 'bootstrap'会找对应的路径
}
},
复制代码
resolve:{
extensions:['.js','.css','.vue'],
mainFields:['style','main'] //先找style字段再找main
},
复制代码
resolve:{
extensions:['.js','.css','.vue'],
mainFields:['style','main'],
modules:[path.resolve(__dirname,node_modules),modle] //modle为自己写的插件或组件
},
复制代码
resolve:{
extensions:['.js','.css','.vue'],
mainFields:['style','main'],
modules:[path.resolve(__dirname,node_modules),modle],
mianFiles:['index.js','main.js']
},
复制代码
以jquery为例来说说webpack引用第三方插件的几种方式:
npm install jquery
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
},
{ //使用babel-loader
test:/\.js$/,
use:'babel-loader',
exclude:'/node_modules/'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
"$":'jquery' //在全局下添加$变量,不需要再次引入
}),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
//index.js
console.log($) //在全局下添加$变量,不需要再次引入import
console.log(window.$) //undefine,不会挂载在window上
复制代码
npm install export-loader -D
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
},
{
test:/\.js$/,
use:'babel-loader',
exclude:'/node_modules/'
},
{
test:/jquery/,
use:{
loader:'expose-loader', //expose-loader暴露$
options:{
$:'jquery'
}
},
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
//index.js
const $ = require('jquery');
console.log($)
console.log(window.$) //能够挂载在window上
复制代码
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
},
{
test:/\.js$/,
use:'babel-loader',
exclude:'/node_modules/'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
externals:{
'jquery':'$' //表明jquery是外链CDN引用
}
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
//index.html
Document
复制代码
devServer: {
contentBase:'./dist',
port:'3000',
hot:true,
proxy:{
target:'http://xxxxx' //代理的服务器
pathRewirte:{
'/xxx':'/yyy'
}
}
},
复制代码
// 把webpack-dev-server的配置引入到自己的本地服务
const express = require('express');
const webpackDevMiddleware = require('webpack-dev-middleware');
// 引入webpack配置文件
const config = require('./webpack.config');
let app = express();
cosnt webpack = require('webpack');
let compiler = webpack(config); //用webpack进行编译
app.use(webpackDevMiddleware(compiler));
app.listen(3000);
复制代码
plugin和loader的区别在于loader只在编译module时执行,而plugin可能在webapck工作流程的各个阶段执行。
清除dist目录
module.exports = {
mode:'production', 生产环境会自动压缩
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
},
{
test:/\.js$/,
use:'babel-loader',
exclude:'/node_modules/'
}
]
},
plugins: [
new CleanWebpackPlugin(['dist/*.*']), //清空dist目录
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
externals:{
'jquery':'$'
}
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
module.exports = {
mode:'production', 生产环境会自动压缩
entry:{
index:'./src/index'
},
output: {
filename:'[name].[hash:8].js',
path: path.resolve(__dirname,'dist')
},
optimization:{
minimizer:{
new UglifyJSPlugin({ //压缩js
cache:true,
parallel:ture, //并行压缩
sourthMap:true, //启动sourthMap
}) ,
new OptimizeCssAssetsPlugin() //压缩css
}
},
module: {
rules:[
{
test:/\.css$/,
use:[{
loader:MiniCssExtractPlugin.loader,
},'css-loader']
},
{
test:/\.jpg|png/,
use:{
loader:'url-loader',
options:{
limit:8*1024
}
}
},
{
test:/\.html$/,
use:'html-withimg-loader'
},
{
test:/\.js$/,
use:'babel-loader',
exclude:'/node_modules/'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename:index.css,
}),
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
chunks:['index']
})
],
externals:{
'jquery':'$'
}
devServer: {
contentBase:'./dist',
port:'3000',
hot:true
},
resolve:{},
}
复制代码
学习webpack的过程是从官方文档开始学习的,照着敲了一遍,然后搜索相关的内容。
作者:梦想攻城狮
链接:https://juejin.im/post/5bbec0395188255c305012d1
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。