內(nèi)容提綱
- electron-vue項目中的webpack工程實例
- 思考
- webpack與gulp/grunt
- HMR
electron-vue項目中的webpack工程實例
從electron-vue項目中的實際使用例子來入手
如下是 webpack.renderer.config.js
文件
'use strict'
process.env.BABEL_ENV = 'renderer'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
let whiteListedModules = ['vue']
let rendererConfig = {
/**
* 控制打包后代碼的形式辅辩,影響調(diào)試。當前設置為cheap-module-eval-source-map,調(diào)試時僅可看到原始源代碼
*(僅限行),如果同時使用了代碼壓縮插件妥泉,代碼被合成一行,將無法獲取有效的調(diào)試內(nèi)容埋虹。
*/
devtool: '#cheap-module-eval-source-map',
/**
* webpack處理項目的入口文件侵贵,以該文件作為構(gòu)建其內(nèi)部依賴圖的開始。
* 通常是項目的 index 或 main 文件
* 注意 entry 可以是一個數(shù)組贴彼,即 webpack 可以處理多個入口文件潜腻,將其
*/
entry: {
renderer: path.join(__dirname, '../src/renderer/main.js')
},
/**
* 聲明外部依賴,在此聲明的文件器仗,即使在工程中使用import引入融涣,也不會被打到bundle中。
* 一般我們會把package.json中的dependencies排除在外精钮。
* 當我們需要使用cdn引用第三方庫時威鹿,我們也要將其放在external中,比如使用jquery cdn
* 思考1:node_modules中的三方庫被排除在打包的bundle文件外轨香,為什么我們的程序在打包后仍然能夠使用
* 三方庫
*/
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
],
/**
* 模塊是 webpack 的核心忽你,webpack 幫我們把文件打包成模塊,然后我們就可以使用 import 的方式來
* 使用臂容。webpack 通過 loader 來各種處理文件科雳,因此我們可以在項目中使用模塊化的方法引用任何類型的文件
*/
module: {
rules: [
{
/**
* test 做正則匹配,所有符合該匹配規(guī)則的文件都將應用該 loader 規(guī)則
*/
test: /\.(js|vue)$/,
/**
* enfore 用來定義規(guī)則的執(zhí)行順序脓杉,取值 ['pre', 'post']糟秘,此處為 pre,將在所有 loader 執(zhí)行前
* 執(zhí)行球散。即在所有 loader 規(guī)則之前蚌堵,使用 eslint-loader 對代碼做靜態(tài)檢查
*/
enforce: 'pre',
/**
* exclude 排除不想應用該規(guī)則的目錄
*/
exclude: /node_modules/,
/**
* useEntry 配置loader
*/
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.css$/,
/**
* ExtractTextPlugin 插件,用于將 *.css 分離到單獨的 style.css 文件中沛婴,而不是作為樣式
* 放入 bundle.js 文件中
* fallback 是當 CSS 沒有被提取時應用的 loader
* style-loader 的作用是使用 <style></style> 標簽將樣式添加到頁面 dom 中
*/
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.scss$/,
/**
* 不同于 use 的形式吼畏,loader 寫成內(nèi)聯(lián)的方式
* 執(zhí)行順序是“從右到左,從下到上“嘁灯,即 sass-loader -> css-loader -> vue-style-loader
* loader 之間通過 ! 分隔泻蚊,如果需要為 loader 配置參數(shù),則可以使用 ?key=value&foo=bar 的形
* 式丑婿,類似于 url 的參數(shù)性雄。
*/
loader: 'vue-style-loader!css-loader!sass-loader'
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.json$/,
use: 'json-loader'
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: process.env.NODE_ENV === 'production',
loaders: {
/**
* 帶參數(shù)的 loader
*/
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
/**
* query 老的寫法没卸,等同于 options
*/
query: {
limit: 10000,
name: 'imgs/[name].[ext]'
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
},
{
test: /\.(ogg)$/,
use: 'file-loader'
}
]
},
plugins: [
new ExtractTextPlugin('styles.css'),
/**
* HtmlWebpackPlugin 這個插件的作用是依據(jù)一個簡單的index.html模板,生成一個自動引用你打包后的JS文
* 件的新index.html秒旋。這在每次生成的js文件名稱不同時非常有用(比如添加了hash值)
* 當我們發(fā)布一個網(wǎng)站應用時约计,我們需要通過生成帶 hash 值的文件名來更新瀏覽器對靜態(tài)文件的緩存
* 這樣會有一個問題,我們每次使用 webpack 打包后都需要手動修改 index.html 文件中引用的
* bundle.[hash].js 文件名迁筛,顯然很麻煩煤蚌。
* HtmlWebpackPlugin 的引入就是為了解決此問題,通過配置一個 index 模板细卧,webpack 會在每次打包完成
* 后尉桩,將 bundle.[hash].js 的引用注入 index.html 中
*/
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: process.env.NODE_ENV !== 'production'
? path.resolve(__dirname, '../node_modules')
: false
}),
/**
* HotModuleReplacementPlugin webpack 提供的最有用的功能之一。它允許在運行時更新各種模塊贪庙,而無需
* 進行頁面刷新蜘犁。使用 HMR 的方法:
* 1.在webpack配置文件中添加HMR插件;
* 2.在Webpack Dev Server中添加“hot”參數(shù)止邮; 在我們的electron-vue應用中使用了接口調(diào)用的方式这橙,查看
* dev-runner.js 文件
*/
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
/**
* 自動加載模塊,并定義到全局變量导披,隨時可以引用屈扎。相當于 import 了一個全局對象
* new webpack.ProvidePlugin({
* identifier: 'module1',
* identifier: ['module1', 'property1'],
* // ...
* });
* 任何時候,當 identifier 被當作未賦值的變量時盛卡,module 就會自動被加載助隧,并且 identifier 會被這個
* module 輸出的內(nèi)容所賦值。
* 支持支持命名導出滑沧,如我們想全局使用 _.map 這個方法并村,那么我們可以這樣來引入:
* new webpack.ProvidePlugin({
* _map: ['lodash', 'map']
* });
* 在項目中即可以使用 _map 來使用 _.map 方法
*/
new webpack.ProvidePlugin({
jQuery: "jquery",
jquery: "jquery",
$: "jquery",
"window.jQuery": "jquery"
})
],
/**
* 定義打包文件名,[name] 參數(shù)由 entry 中的key決定滓技,此處為render.js
* 這里文件名沒有使用 hash 值哩牍,是因為在 electron 項目中,升級不需要考慮瀏覽器緩存的問題令漂,整個應用都會
* 被替換
*/
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
/**
* 在此可以定義 import 或 require 的別名膝昆,來確保模塊引入變得更簡單。
* 當目錄下的文件沒有文件后綴時叠必,使用 extensions 中的后綴依次嘗試解析
*/
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js',
'config' : path.join(__dirname, '../config'),
'components': path.join(__dirname, '../src/renderer/components'),
'utils': path.join(__dirname, '../src/renderer/utils'),
'renderer': path.join(__dirname, '../src/renderer'),
'services': path.join(__dirname, '../src/renderer/services'),
'store': path.join(__dirname, '../src/renderer/store'),
'router': path.join(__dirname, '../src/renderer/router'),
'plugins': path.join(__dirname, '../src/renderer/plugins'),
'css': path.join(__dirname, '../src/renderer/css'),
'images': path.join(__dirname, '../src/renderer/images'),
'jquery': path.join(__dirname, '../node_modules/jquery/src/jquery')
},
extensions: ['.js', '.vue', '.json', '.css', '.node']
},
/**
* 構(gòu)建目標, https://webpack.docschina.org/configuration/target
*/
target: 'electron-renderer'
}
/**
* Adjust rendererConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
/**
* DefinePlugin 允許創(chuàng)建一個在編譯時可以配置的全局常量荚孵。
* 比如定義后端接口的url
* https://webpack.docschina.org/plugins/define-plugin/
*/
rendererConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust rendererConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
/**
* 這是一款基于 Babel 的壓縮工具,支持 es6 的一些特性纬朝,取代 UglifyJS
*/
new BabiliWebpackPlugin({
removeConsole: true,
removeDebugger: true
}),
/**
* Copies individual files or entire directories to the build directory.
* 一般會使用插件把需要使用的靜態(tài)文件考到構(gòu)建后對應的目錄
*/
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/electron/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
/**
* 用于 webpack2 對 webpack1 配置的兼容收叶,抹平差異
*/
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = rendererConfig
思考1:external
node_modules中的三方庫被排除在打包的bundle文件外,為什么我們的程序在打包后仍然能夠使用三方庫
// package.json
{
...
"build": {
...
"files": [
"dist/electron",
"node_modules/",
"package.json"
]
}
}
在 package.json 文件中共苛,配置了 files 文件判没,electron-build 在打包時會將 files 中的文件夾全部打包到工程中蜓萄,即 resources/app.asar
文件
我們使用命令查看
asar list ./resources/app.asar
可以看到如下一系列文件
/node_modules/argparse/lib/action/store/false.js
/node_modules/argparse/lib/action/store/true.js
/node_modules/argparse/lib/action/append
/node_modules/argparse/lib/action/append/constant.js
...
/dist
/dist/electron
/dist/electron/index.html
/dist/electron/main.js
/dist/electron/renderer.js
/dist/electron/static/update/process.html
/dist/electron/static/update/process.js
/dist/electron/static/update/libs
...
/dist/electron/fonts
/dist/electron/fonts/element-icons.ttf
webpack與gulp/grunt
Gulp/Grunt是一種能夠優(yōu)化前端的開發(fā)流程的工具,而WebPack是一種模塊化的解決方案澄峰,不過Webpack的優(yōu)點使得Webpack在很多場景下可以替代Gulp/Grunt類的工具
Grunt和Gulp的工作方式
在一個配置文件中嫉沽,指明對某些文件進行類似編譯,組合俏竞,壓縮等任務的具體步驟绸硕,工具之后可以自動替你完成這些任務。
Webpack的工作方式是
把你的項目當做一個整體胞此,通過一個給定的主文件(如:index.js)臣咖,Webpack將從這個文件開始找到你的項目的所有依賴文件跃捣,使用loaders處理它們漱牵,最后打包為一個(或多個)瀏覽器可識別的JavaScript文件。
如果實在要把二者進行比較疚漆,Webpack的處理速度更快更直接酣胀,能打包更多不同類型的文件。
開發(fā)模式熱更新
開發(fā)模式熱更新分為兩部分:
- 監(jiān)控代碼修改娶聘,并自動重新編譯打包
- 通知瀏覽器同步修改的內(nèi)容(注意闻镶,不是刷新瀏覽器重新加載,是動態(tài)替換部分代碼丸升,保持原頁面的數(shù)據(jù)狀態(tài)铆农,這是HMR的關鍵特性)
通常使用webpack-dev-server
- 該工具包括自動監(jiān)控代碼編譯,以及HMR狡耻。
-
webpack-dev-server
將代碼打包在內(nèi)存中墩剖,所以修改代碼時在項目下找不到動態(tài)生成的編譯文件。
注意
依靠HMR的熱更新機制夷狰,我們可以享受修改的文件被實時同步到瀏覽器的便利岭皂,但對于頁面來說,這并不是全部沼头。我們?nèi)匀恍枰陧撁娴膉s代碼中做一些處理爷绘。
比如:我們在按鈕上綁定了一個click事件的處理函數(shù)handleClick
,我們修改了handleClick
的代碼进倍,它被推送到瀏覽器中土至,但是頁面并不會自動替換之前綁定的click事件,當我們點擊按鈕時猾昆,仍然得到舊的響應陶因。
所以除了使用webpack-dev-server
毡庆,我們需要在js代碼中監(jiān)聽HMR 的 accept 方法烙如,在此方法中更新類似的處理函數(shù)亚铁。
這也就是為什么在react項目中螟加,我們需要使用react-hot-loader
庫捆探,本質(zhì)上是實現(xiàn)了HMR 的 accept 的處理。