好久没有写文章,最近在做项目自动化构建工具的迁移,花了一点时间去研究 webpack
,webpack
的入门其实简单,但是现有的资料比较零碎,按照我的学习路径整理了下,希望对大家能有所帮助。
接下来我将从3个部分来给大家介绍webpack
,分别是 webpack 的基础配置,哪些常用的加载器,我在项目自动化构建工具改造的过程中雨大了那些问题
webpack 基础配置
首先我们需要理解四个重要的概念
-
入口(你需要打包的文件声明),你的项目需要什么依赖没在这里进行声明,require 你需要的依赖,webpack 会直接和间接的找到依赖文件进行打包,可传字符串,数组,对象
// 配置了3个入口文件
entry: [ './config/dependencies.js', './config/index.js', './config/cssImport.js'
]
-
出口(你打包资源后到哪个目录哪个文件),声明依赖打包后的文件输出的目录及命名方式,可传字符串,数组,对象
//声明了依赖压缩打包之后会被添加到 build 目录的 bundle.js 文件里 output: { path: path.join(__dirname, '../build'), filename: 'bundle.js', },
-
loader(模块加载器)能将各种资源的依赖模块打包成webpack 能够理解的 js 模块,从而进行你需要的操作,如 css 预编译,图片压缩,路径转换等
loaders: [{ test: /\.js?$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader', // 'babel-loader' is also a legal name to reference query: { presets: ['es2015'] } }]
-
插件(插件用于扩展 loader 的能力)你可以用插件进行定义环境变量对代码进行打包压缩。
//声明 const ImageminPlugin = require("imagemin-webpack-plugin").default const CopyWebpackPlugin = require('copy-webpack-plugin') //从imgSrc 目录压缩图片,压缩完拷贝到 build/img 目录下 plugins: [ new CopyWebpackPlugin([ { from: 'imgSrc' ,to:'img'} ]), new ImageminPlugin( { test: /\.(jpe?g|png|gif|svg)$/i } ) ]
webpack 的常用加载器
loader 用于常用的源代码进行装换,常用的有js编译,css 预编译,图片压缩等,这些都是项目中比较常见的,大家平时不需要记忆,只要能大概知道有这么一个东西,需要用到的时候去查阅就行,loader 官方收录文档
在项目迁移中遇到的问题
由于各种历史原因,我们的项目目录结构凌乱,项目依赖多,结构复杂,使用着 angular + gulp
进行开发,自动化构建工具还处于刀耕火种的年代,发版本的时候通过前端给包,不安全,不规范。新的一年我们尝试去改变这种现状,洗完通过运维直接拉去前端代码,这样能充分的保证前端代码的一致性。
目前的项目结构的控诉
- 项目所有问文件处于一个平级状态
- 有3个依赖包文件夹分别是 framwork,lib,node_modules(各种历史遗留问题)
- 源图片imgSrc和压缩后的img在同一个目录
- 人工手动打包的时候要很小心的删除不必要的文件
现有的改进方案
- 创建 build 文件夹,nginx 代理 到 build 目录
- config 中配置 webpack 构建打包任务,以及各种依赖的入口文件
- ib && framwork 移到 build 文件
- less 中对图片的引用路径需要变动,因为img 的路径变动了,tpls 也变动了,所以在视图中直接引用的不需要变化
- gulpfile 中的 js 压缩,less 编译,图片压缩内容迁移到 webpack 任务中
- 通过 npm run dev 进行编译构建
- 图片压缩
- 依赖及模块文件变化时实时构建项目
- 弃用 gulpfile 编译构建
创建 build
文件夹,nginx
代理 到 build
目录,配置生产环境依赖目录,运维可以直接将代理指向该目录,不需要人工手动剔除多余的目录
config 中配置 webpack 构建打包任务,以及各种依赖的入口文件
建立config 目录,存放 webpack 配置文件目录,及各种依赖声明目录,我们一共声明了3个依赖入口文件,分别是npm 管理的生产依赖文件,css 文件,项目逻辑js文件。css 和 生产依赖的内容比较烧,我们手动 require 一下,但是我们项目是已经经过近两年的迭代开发,逻辑代码 js 文件繁多,目录结构负责,所以我们需要写一个简单的 node 脚本,递归查询项目目录结构,动态 require 写入到我们的js入口文件中,node 脚本 如下
var files = fs.readdirSync('js')
var jsPath = 'js'
fs.unlink('config/index.js')
var getFileName = function (files,dirPath) {
files.forEach(function (filename) {
var fullname = path.join(dirPath,filename)
var stats = fs.statSync(fullname)
if (stats.isDirectory()){
var subFiles = fs.readdirSync(fullname)
getFileName(subFiles,fullname)
} else {
let file = './../'+dirPath+'/'+filename
fs.writeFile('./config/index.js', 'require (\"'+file +'\")\n', {
flag: 'a'
}, function(err){
if(err) throw err
})
}
})
}
getFileName(files,jsPath)
配置我们的入口和输入
entry: [
'./config/dependencies.js',
'./config/index.js',
'./config/cssImport.js'
],
output: {
path: path.join(__dirname, '../build'),
filename: '[name].js',
},
然后是对我们的gulp task 进行迁移,我们需要配置 js 语法编译,css 预处理,图片压缩的功能。js 编译只需要使用 babel-loader
,通过 npm install babel-loader
安装加载器,指定匹配的文件及需要忽略的文件,指定转化语法,设定转码规则,就配置成功了
{
test: /\.js?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader', // 'babel-loader' is also a legal name to reference
query: {
presets: ['es2015']
}
}
css 预编译,我们使用了 less 做为 css 开发工具,编译的时候需要用到的 loader 比较多,通过less-loader
,css-loader
,style-loader
的链式调用,将样式作用于DOM
rules: [{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}]
或则
loaders: [
{
test: /\.less$/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
//支持@important引入css
importLoaders: 1
}
},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
//一定要写在require("autoprefixer")前面,否则require("autoprefixer")无效
require('postcss-import')(),
require("autoprefixer")({
"browsers": ["Android >= 4.1", "iOS >= 7.0", "ie >= 8"]})]
}
}
},
'less-loader']
}]
图片压缩我们选择了两个插件分别是imagemin-webpack-plugin
和copy-webpack-plugin
,imagemin 实现图片压缩,copy 实现图片资源拷贝,具体配置如下
//插件引用
const ImageminPlugin = require("imagemin-webpack-plugin").default
const CopyWebpackPlugin = require('copy-webpack-plugin')
//插件使用
plugins: [
new CopyWebpackPlugin([
{ from: 'imgSrc' ,to:'img'}
]),
new ImageminPlugin(
{ test: /\.(jpe?g|png|gif|svg)$/i }
)
]
了解完细节我们看下一个整体的配置
const fs = require ("fs")
const path = require ("path")
const ImageminPlugin = require("imagemin-webpack-plugin").default
const CopyWebpackPlugin = require('copy-webpack-plugin')
var files = fs.readdirSync('js')
var jsPath = 'js'
fs.unlink('config/index.js')
var getFileName = function (files,dirPath) {
files.forEach(function (filename) {
var fullname = path.join(dirPath,filename)
var stats = fs.statSync(fullname)
if (stats.isDirectory()){
var subFiles = fs.readdirSync(fullname)
getFileName(subFiles,fullname)
} else {
let file = './../'+dirPath+'/'+filename
fs.writeFile('./config/index.js', 'require (\"'+file +'\")\n', {
flag: 'a'
}, function(err){
if(err) throw err
})
}
})
}
getFileName(files,jsPath)
module.exports = {
entry: [
'./config/dependencies.js',
'./config/index.js',
'./config/cssImport.js'
],
output: {
path: path.join(__dirname, '../build'),
filename: 'bundle.js',
},
module: {
loaders: [
{
test: /\.js?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader', // 'babel-loader' is also a legal name to reference
query: {
presets: ['es2015']
}
},{
test: /\.less$/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
//支持@important引入css
importLoaders: 1
}
},{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
//一定要写在require("autoprefixer")前面,否则require("autoprefixer")无效
require('postcss-import')(),
require("autoprefixer")({
"browsers": ["Android >= 4.1", "iOS >= 7.0", "ie >= 8"]})]
}
}
},
'less-loader']
},{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}]
}]
},
plugins: [
new CopyWebpackPlugin([
{ from: 'imgSrc' ,to:'img'}
]),
new ImageminPlugin(
{ test: /\.(jpe?g|png|gif|svg)$/i }
)
]
}
实现热编译
实现完配置之后我们要考虑另一个问题了,我们希望修改完之后能实时编译预览,不用每次都手动的跑一边命令,我们可以选择 webpack 提供的 node 服务,webpack-dev-server
,在配置文件里新增一个 webpack.server.js 的文件,在文件中 require 需要的依赖,webpack-dev-server
可以指定服务启动的目录,及服务监听的端口,我们的配置如下
var path = require("path")
var webpack = require("webpack")
var webpackDevServer = require("webpack-dev-server")
var webpackCfg = require("./webpack.config.js")
var compiler = webpack(webpackCfg)
//init server
var app = new webpackDevServer(compiler, {
contentBase : path.join(__dirname, '../build'),
noInfo : true,
hot : true,
historyApiFallback : true,
stats : { colors : true },
//注意此处publicPath必填
publicPath: webpackCfg.output.publicPath
})
app.listen(9090, "localhost", function (err) {
if (err) {
console.log(err)
}
})
console.log("listen at http://localhost:9090")
启动我们的服务
配置完成之后,我们需要启动我们的服务,你可以再你的控制台中直接输入 node config/webpack.server.js
来启动服务,webpack --config config/webpack.config.js
来进行编译构建,也可以在 package.json 文件中配置 script 如
"scripts": {
"start":"node config/webpack.server.js",
"build": "webpack --config config/webpack.config.js"
},
这样你就可以通过 npm start
和 npm run build
来控制你的服务启动和项目构建
参考文章列表
如果你还想更深入理解,你可以继续阅读这些扩展文章
理解概念
配置讲解
常会用到的loaders
webpack打包原理
webpack全局变量
less 图片解析问题
其他优秀的配置文章
《使用webpack-dev-server实现热更新》
imagemin-webpack-plugin
copy-webpack-plugin
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。