webpack基本使用,打包html,scss预处理器,autoprefixer浏览器前缀,使用jquery,代码压缩,命令行自动生成html和js
项目git地址-https://github.com/zshuai34/webpack3-Multi-html-pages
本文基本内容介绍
学习使用webpack
打包 html多页面文件时,整理的webpack使用方法
- 打包单个
js
文件,生成hash
文件名; - 使用
html-webpack-plugin
打包html css-loader
打包css,安装使用scss
预处理器,使用Autoprefixer
添加浏览器css前缀webpack
中使用jquery
方法- 命令行生成
html&js
- 最后有完整的
webpack
配置- 完整
webpack.config.js
配置 - 完整的
package.json
postcss.config.js
- 完整
学习webpack
中,一些解决方法来自网络
目录
安装
// 全局安装webpack
npm i webpack -g
// 项目中安装
npm i webpack webpack-cli webpack-dev-server -D
打包基本使用
打包js
// 打包单个js
const path = require('path');
module.exports = {
entry: './js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
// 打包多个js为一个js
const path = require('path');
module.exports = {
entry: [
index: './js/index',
index2: './js/index2'
],
output: {
path: __dirname + '/dist/js',
filename: '[name].js'
// name:js原名称 /hash随机hash值 /chunkhash:只要文件没有改变,就不会生成新的hash
}
};
babel对es6代码进行转换
npm i babel-cli@6.26.0 babel-loader@6.26.0 babel-core@7.1.4 babel-preset-env@1.6.1 -D
{//js loader
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
oader: 'babel-loader'
}
}
打包html
使用 html-webpack-plugin
插件
var path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
index: './js/index',
index2: './js/index2'
},
output: {
path: __dirname + '/dist/js',
filename: '[name].js' // 随机名称 hash /chunkhash:只要文件没有改变,就不会生成新的hash
},
plugins: [
new HtmlWebpackPlugin({
title: '首页',
template: 'index.html', // 源模板文件
filename: __dirname + '/dist/index.html', // 输出文件
hash: true, // 添加webpack每次编译产生的唯一hash值 js?jskadjsjd
inject: 'body', // 向template或者templateContent中注入所有静态资源,不同的配置值注入的位置不经相同。
chunks: ['index'] //在配置多个页面时,每个页面注入的cthunk应该是不相同的,需要通过该配置为不同页面注入不同的cthunk;
}),
new HtmlWebpackPlugin({
title: '我的页面2',
template: 'index2.html',
filename: __dirname + '/dist/index2.html',
chunks: ['index2']
})
]
}
打包图片
安装 url-loader
npm i url-loader@1.1.2 -D
如果图片大小超过设置的转换base64限制,就需要安装 npm i file-loader@6.1.1 -D
,否则会报错
{// img 压缩,,生成hash值
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
esModule: false,
limit: 1024 * 50, // 50KB以上压缩
outputPath: './img',
name: '[name].[hash:8].[ext]'
}
}
]
}
需要对页面中引入的图片进行特殊处理
安装html-withimg-loader
npm i html-withimg-loader@0.1.16 -D
module: {
rules: [
{
test: /\.(html)$/,
loader: 'html-withimg-loader',
}
]
}
package.json 中依赖包
新建空文件夹,cmd命令执行 npm init
自动生成 package.json
文件,粘贴 devDependencies
到package.json
中,执行 npm install
安装依赖包
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^0.28.10",
"expose-loader": "^0.7.5",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.0.6",
"jquery": "^1.11.3",
"open-browser-webpack-plugin": "0.0.5",
"style-loader": "^0.20.3",
"uglifyjs-webpack-plugin": "^1.2.3",
"url-loader": "^1.0.1",
"webpack": "^3.5.5",
"webpack-dev-server": "^1.14.1"
}
-
babel
js语法: es6 => es5npm install -D babel-loader @babel/core @babel/preset-env webpack
-
clean-webpack-plugin
构建时删除构建目录 -
css-loader
必须要配合使用style-loader
,css-loader的作用是帮我们分析出各个css文件之间的关系,把各个css文件合并成一段css; -
style-loader
style-loader的作用是将css-loader生成的css代码挂载到页面的header部分,多个loader配合使用时,处理顺序是:从下到上,从右到左 的顺序 -
expose-loader
暴露全局属性方法,例如jquery
-
extract-text-webpack-plugin
为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象在webpack4中,建议用mini-css-extract-plugin替代 -
file-loader
打包图片,字体等文件,生成一个随机的hash值作为图片的名字 -
html-webpack-inline-source-plugin
依赖于html-webpack-plugin
npm i -D html-webpack-inline-source-plugin html-webpack-plugin
-
html-webpack-plugin
打包html,把 js、css 等代码内联进 html 中new htmlWebpackPlugin({ //有几个生成new几个html,生成html filename: 'index.html', title: '首页', template: 'index.html', chunks: ['app', 'flexible75'],//html需要引入的js cache: true,//只有在内容变化时才会生成新的html minify: { removeComments: true, //是否压缩时 去除注释 collapseWhitespace: false // 去除空格压缩 } })
-
jquery
引入的jquery js -
open-browser-webpack-plugin
自动打开浏览器插件 -
uglifyjs-webpack-plugin
代码压缩插件 -
url-loader
类似file-loader
-
webpack-dev-server
一个服务器插件,相当于webpack+apache,启动一个web服务并实时更新修改
问题:html中img src引入图片不打包
解决方法:使用 html-withimg-loader
webpack.config.js
中配置
module: {
rules: [
{
test: /\.(html)$/,
loader: 'html-withimg-loader',
}
],
}
使用scss
安装scss
安装css和sass插件
npm install sass-loader@7.3.1 node-sass@4.14.1 css-loader@0.28.10 expose-loader@0.7.5 style-loader@0.20.3 --save-dev
如果出现报错 Module build failed: ReferenceError: window is not defined
,则降低sass版本
- 先卸载
npm uninstall sass-loader
- 再安装
npm install sass-loader@7.3.1 --save-dev
webpack.config.js配置
官方方法:
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
},
],
}
使用extract-text-webpack-plugin
提取css样式,通过link方式引入html中
npm i extract-text-webpack-plugin@3.0.2 -D
module: {
rules: [
{//css loader
test: /\.(css|s[ac]ss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader']
})
}
]
}
使用 autoprefixer
Autoprefixer解析CSS文件并且添加浏览器前缀到CSS规则里,使用Can I Use的数据来决定哪些前缀是需要的。
安装autoprefixer
npm i autoprefixer@9.8.6 -D
根目录新建postcss.config.js
文件
module.exports = {
plugins: [
require('autoprefixer')
]
}
webpack.config.js配置
{//css loader
test: /\.(css|s[ac]ss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader', 'postcss-loader']
})
}
package.json配置
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
安装jquery
npm安装jquery
npm i jquery@1.11.3 -S
在页面js中通过 var jquery = require('jquery')
引入
此方法会在所有使用jquery的js 中都会打包进jquery
webpack.config.js
module: {
rules: [
{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: 'jQuery'
},{
loader: 'expose-loader',
options: '$'
}]
}
]
},
plugins: [
// 全局引入jquery
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
]
CND引入jquery
建议在页面中通过CDN方式引入jquery
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
命令行生成html&js
安装依赖包
babel-node 是 babel-cli 的附带工具,所以只要安装了 babel-cli ,就可以直接使用 babel-node
- 安装
babel-cli
:npm i babel-cli -D
- 安装
read
:npm i read -D
- 安装
colors
:npm i colors -D
package.json
中scripts
添加 new
命令
"scripts": {
"new": "babel-node build/new-page.js"
}
build目录
-build
- templates // 页面模板
- page-new.html // html模板,按自己需求写入,如:CDN引入jquery
- page-new.js // js模板,按自己需求写入
- new-page.js // 生成页面模板命令方法
new-page.js
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const read = require('read')
require('colors')
const fileCharset = 'utf-8'
let pageTitle = ''
let pageName = null
let flagCount = 2
const waitForPageTitle = (prompt) => {
read({
prompt: prompt || '请输入新增页面标题 : ',
timeout: 60000
}, (error, result, isDefault) => {
if (error) {
if (error.message === 'canceled') {
console.log('\n\n您已取消该操作!'.yellow)
process.exit(0)
} else if (error.message === 'timed out') {
console.log('等待已超时, 请重新输入!'.yellow)
process.exit(0)
} else {
throw error
}
}
if (result && result !== '') {
pageTitle = result
}
waitForPageName()
})
}
waitForPageTitle()
const waitForPageName = (prompt) => {
read({
prompt: prompt || '请输入新增页面文件名: ',
timeout: 60000
}, (error, result, isDefault) => {
if (error) {
if (error.message === 'canceled') {
console.log('\n\n您已取消该操作!')
process.exit(0)
} else if (error.message === 'timed out') {
console.log('等待已超时, 请重新输入!')
process.exit(0)
} else {
throw error
}
}
if (!result || result === '') {
waitForPageName('页面文件名不能为空,请重新输入: '.yellow)
} else {
pageName = result
validPageName()
}
})
}
const validPageName = () => {
const projectRoot = path.resolve(__dirname, '../')
const pageDir = path.resolve(projectRoot, `./src/pages/${pageName}`)
const exists = fs.existsSync(pageDir)
if (exists) {
console.log(`该页面已存在: ${pageDir}`.yellow)
waitForPageName('请重新输入新增页面文件名:')
} else {
fs.mkdirSync(pageDir)
createPage(projectRoot, pageDir, pageName)
}
}
const createPage = (projectRoot, pageDir, pageName) => {
const sourceHtml = path.resolve(projectRoot, 'build/templates/page-new.html')
const sourceJS = path.resolve(projectRoot, 'build/templates/page-new.js')
const destHtml = path.resolve(pageDir, `${pageName}.html`)
const destJS = path.resolve(pageDir, `${pageName}.js`)
copyFile(sourceHtml, destHtml, (destFile) => {
flagCount--
ensureSuccess()
})
copyFile(sourceJS, destJS, (destFile) => {
flagCount--
ensureSuccess()
})
}
const ensureSuccess = () => {
if (flagCount === 0) {
const tip = `${pageName} 页面新增成功! 标题为: ${pageTitle}`
console.log(tip.green)
// 页面生成成功提示
const tipConfig = '请在webpack.config.js中配置 pagesEntryHtml和 pagesEntryJs 来打包html和js'
console.log(tipConfig.green)
process.exit(0)
}
}
const copyFile = (source, dest, cb) => {
fs.readFile(source, fileCharset, (err, data) => {
if (err) throw err
data = data.replace(/\${TITLE}/g, pageTitle)
fs.writeFile(dest, data, fileCharset, (err) => {
if (err) throw err
cb(dest)
})
})
}
page-new.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=EDGE"/>
<meta name="viewport" content="initial-scale=1, user-scalable=no, width=device-width">
<meta name="format-detection" content="telephone=no">
<meta http-equiv="Pragma" content="no-cache">
<title>${TITLE}</title>
</head>
<body>
${TITLE}
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
</body>
</html>
page-new.js
/**
* @author zs
* 在此js中引入页面私有的css文件
*/
console.log('~_~ jQuery can use directly ~_~')
console.log(jQuery)
console.log('add your code here ....')
完整的webpack配置
项目目录
- dist // 打包后的文件
- src // 开发目录
- css // 可以使用css/scss
- main.scss
- about.css
- img
- js
- pages // 页面文件
- about
- about.html
- about.js
- index.html
- package.json
- package-lock.json
- postcss.config.js // autoprefixer 配置文件
- webpack.config.js // webpack配置
webpack.config.js 配置
var webpack = require('webpack');
var path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');
var cleanWebpackPlugin = require('clean-webpack-plugin');
var uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// var OpenBrowserPlugin = require('open-browser-webpack-plugin');
/**
* html-webpack-plugin 构建HTML文件
* clean-webpack-plugin 构建时清空 build目录
* uglifyjs-webpack-plugin 代码压缩插件
* extract-text-webpack-plugin 抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象
* open-browser-webpack-plugin 自动打开浏览器插件
*/
const pagesPath = './src/pages/'
devWebpackConfig = {
entry: { //入口
app: './src/app.js',
common: [
'./src/js/flexible75.js'
]
},
output: {//出口
path: path.resolve(__dirname, 'dist'), // URL 以 HTML 页面为基准
publicPath: '',//cdn 按需加载或加载外部资源
filename: 'js/[name].[hash].js'
},
devServer: { //服务
contentBase: './dist', // 告诉服务器从哪里提供内容。只有在你想要提供静态文件时才需要。
host: 'localhost',
hot: true, // 启用 webpack 的模块热替换特性
inline: true, //推荐使用模块热替换的内联模式
progress: true,//显示打包速度
port: 8081,
proxy: {//代理
"/api": {//请求api下的接口都会被代理到 target: http://xxx.xxx.com 中
target: 'http://aixiaiodou.cn:3000',
changeOrigin: true,
secure: false,// 如果是 https,需要配置
pathRewrite: {'^/api': ''}, // 如果不想使用/api 可以重写
bypass: function (req, res, proxyOptions) {
if (req.headers.accept.indexOf('html') !== -1) {
console.log('Skipping proxy for browser request.');
return '/index.html';
}
}
}
}
},
module: {
rules: [
{//css loader
test: /\.(css|s[ac]ss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader', 'postcss-loader']
})
},
{//js loader
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
},
{// img 压缩,,生成hash值
test: /\.(png|svg|jpg|gif)$/,
use: "file-loader?name=[name][hash].[ext]&publicPath=../img/&outputPath=./img"
/*name=[name].[ext]文件名,publicPath=../css中路径,outputPath=./img打包后的生成地址*/
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
},
{
test: /\.(html)$/,
loader: 'html-withimg-loader',
}
]
},
devtool: 'cheap-module-eval-source-map', // 转换过的代码
plugins: [
// new htmlWebpackPlugin({ //有几个生成new几个html,生成html
// filename: 'index.html',
// title: '首页',
// template: pagesPath + 'index.html',
// chunks: ['app'],//html需要引入的js
// cache: true,//只有在内容变化时才会生成新的html
// minify: {
// removeComments: true, //是否压缩时 去除注释
// collapseWhitespace: false // 去除空格压缩
// }
// }),
new cleanWebpackPlugin(['dist']), //构建时清除dist目录
// 全局引入jquery
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
new uglifyjsWebpackPlugin(),
new ExtractTextPlugin({ //提取css
filename: 'css/[name].[hash].css',
disable: false,
allChunks: true
}),
new webpack.optimize.CommonsChunkPlugin({ //打包公共js
name: 'common',
chunks: ['./src'],
minChunks: 2,
minChunks: Infinity
}),
new webpack.HashedModuleIdsPlugin()
// new OpenBrowserPlugin({ url: 'http://localhost:8081'}) //自动打开浏览器
]
};
/**
* 公共js放到 app.js 中
* 要添加页面私有js,添加后在htmlWebpackPlugin-chunks中添加即可
* 如果需要添加样式,在私有js中require
* @type {string[]}
*/
const pagesEntryJs = ['about']
pagesEntryJs.map(pJs => {
devWebpackConfig.entry[pJs] = `${pagesPath + pJs}/${pJs}.js`
})
/**
* 要添加的页面
* filename:文件名称
* template:页面路径
* chunks: [] 页面引入的私有js,需要在devWebpackConfig-entry中配置, common为公共js包括:【flexible75】
* title: 页面标题 <%= htmlWebpackPlugin.options.title %>
* @type {({template: string, filename: string, chunks: [string], title: string}|{template: string, filename: string, chunks: [string, string], title: string})[]}
*/
const pagesEntryHtml = [
{filename: 'index', template: 'index', chunks: ['common', 'app']},
{filename: 'about', template: 'about/index', chunks: ['common', 'app', 'about']}
]
pagesEntryHtml.map(page => {
const plugin = new htmlWebpackPlugin({
// title: page.title,
filename: page.filename + '.html',
template: pagesPath + page.template + '.html',
chunks: page.chunks,
cache: true, //只有在内容变化时才会生成新的html
minify: {
removeComments: true, //是否压缩时 去除注释 生产环境部署时,可以压缩去掉空格和注释
collapseWhitespace: false // 压缩去除空格和换行符
}
})
devWebpackConfig.plugins.push(plugin)
})
module.exports = devWebpackConfig
完整的package.json
{
"name": "webpack3",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "webpack --watch",
"build": "webpack",
"dev": "webpack-dev-server --hot --inline --open"
},
"keywords": [],
"author": "zs",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^9.8.6",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^0.28.10",
"expose-loader": "^0.7.5",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.0.6",
"html-withimg-loader": "^0.1.16",
"open-browser-webpack-plugin": "0.0.5",
"postcss-loader": "^3.0.0",
"sass": "^1.26.10",
"sass-loader": "^7.3.1",
"style-loader": "^0.20.3",
"uglifyjs-webpack-plugin": "^1.2.3",
"url-loader": "^1.0.1",
"webpack": "^3.12.0",
"webpack-dev-server": "^1.14.1"
},
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
],
"dependencies": {
"jquery": "^1.11.3"
}
}
postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
注:
- 基本配置来自 webpack3,对此配置进行的一些优化,包括打包html,和entry中打包的js文件
- 命令行生成页面方法来自 QuickStart-jQuery-webpack