学习使用webpack
打包 html多页面文件时,整理的webpack使用方法
js
文件,生成hash
文件名;html-webpack-plugin
打包htmlcss-loader
打包css,安装使用 scss
预处理器,使用Autoprefixer
添加浏览器css前缀webpack
中使用jquery
方法html&js
webpack
配置
webpack.config.js
配置package.json
postcss.config.js
学习webpack
中,一些解决方法来自网络
// 全局安装webpack
npm i webpack -g
// 打包单个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
}
};
使用 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']
})
]
}
新建空文件夹,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 => es5
npm 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-withimg-loader
npm - html-withimg-loader
webpack.config.js
中配置
module: {
rules: [
{
test: /\.(html)$/,
loader: 'html-withimg-loader',
}
],
}
npm - sass-loader
npm install sass-loader sass --save-dev
如果出现报错 Module build failed: ReferenceError: window is not defined
,则降低sass版本
npm uninstall sass-loader
npm install [email protected] --save-dev
官方方法:
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',
],
},
],
}
module: {
rules: [
{
//css loader
test: /\.(css|s[ac]ss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader']
})
}
]
}
Autoprefixer解析CSS文件并且添加浏览器前缀到CSS规则里,使用Can I Use的数据来决定哪些前缀是需要的。
npm - autoprefixer
npm i autoprefixer
postcss.config.js
文件module.exports = {
plugins: [
require('autoprefixer')
]
}
{
//css loader
test: /\.(css|s[ac]ss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader', 'postcss-loader']
})
}
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
npm i [email protected] -S`
此方法会在所有使用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"
}),
]
建议在页面中通过CDN方式引入jquery
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.11.3/jquery.min.js">script>
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
- templates // 页面模板
- page-new.html // html模板,按自己需求写入,如:CDN引入jquery
- page-new.js // 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)
})
})
}
<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>
/**
* @author zs
* 在此js中引入页面私有的css文件
*/
console.log('~_~ jQuery can use directly ~_~')
console.log(jQuery)
console.log('add your code here ....')
- 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配置
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
{
"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"
}
}
module.exports = {
plugins: [
require('autoprefixer')
]
}
注: