以前只知道webpack很強大,可是一直沒有深刻學習過,此次從頭看了一下教程,而後從0開始搭建了一個多入口網站的開發腳手架,期間遇到過不少問題,因此有心整理一下,但願能給你們一點幫助。javascript
假如咱們接到這樣一個任務,開發一個簡單的官網,好比只有十幾個html頁面。項目很簡單,咱們沒有必要使用什麼大型框架,可是若是隻是傳統的寫幾個html、js和css,確定會遇到這幾個問題:css
看到這裏,可能有的同窗就急了,別廢話,感受進入正題吧,不!咱們先定目標!不管作什麼事情,都要先定目標,而不是幹到哪裏算哪裏,這樣是不會有大的提高的,正是在邁向目標的路上克服各類問題,咱們纔有進步,在進行這個腳手架搭建以前,我但願它是這樣的html
好了,目標定了,開工前端
別急,咱們先來捋一捋目錄,彆着急寫代碼,一個好的目錄,能讓咱們思路清晰,個人目錄結構以下vue
+ config //環境變量配置文件,開發模式和生產模式使用不一樣的環境變量,好比接口地址,開發環境用的接口域名是http://a.com,生產環境使用的是http://b.com
- dev.env.js //本地開發變量
- prod.env.js //生產環境變量
+ src
+ css //本身的less組件或者第三方css庫
+ component //本身組件的less
+ lib //第三方的css庫,好比bootstrap
+ html //html代碼,主要是一些模板,如頭部導航,底部通欄,側邊欄
+ tpl //模板文件
+ img //圖片文件
+ js //本身的js組件庫或者第三方js庫
+ mod //本身的js組件放這裏
+ lib //第三方js庫
+ page //頁面文件
+ index //這個根據本身狀況設置,有的頁面相關性強,能夠放到一個文件夾下,好比一個user文件夾,能夠放我的中心的全部頁面
- index.html //每一個頁面都要有一個html
- index.js //每一個頁面都要有一個js,名稱和html的名稱保持一致
- index.less //每一個頁面都要有一個同名less文件
+ test
- test.html
- test.js
- test.less
+ webpack //webpack的配置文件
- dev-server.js //開發服務設置,能夠經過localhost訪問頁面,頁面的實時編譯
- webpack.common.js //開發環境和生產環境通用配置
- webpack.dev.js //開發環境特有的配置
- webpack.prod.js //生產環境特有的配置
複製代碼
首先是config目錄,目前我主要放一些環境變量,就是開發環境和生產環境所不一樣的變量,好比接口地址,咱們開發的時候,用本地的api接口地址,而打包的時候,要換成生產環境api地址。java
webpack目錄存放webpack的配置文件,其中開發和生產通用配置 放到webpack.common.js中,開發特有配置放到webpack.dev.js中,生產特有配置放到webpack.prod.js中。node
src是咱們開發的主目錄,其中page目錄放置咱們的頁面文件,這裏可能和平時有所不一樣,我把每一個頁面用到的html、js和less文件放到了一塊兒,有的同窗可能把全部html放到一個目錄下,js放到一個目錄下,可是這樣存在一個問題,每次改動頁面,都要去翻目錄,很是的不方便,咱們應該把這種高度相關的文件放到一塊兒,而提取的各類css組件或js組件能夠和頁面分開放置。react
github地址:github.com/501351981/w…webpack
webpack支持多入口,即給定多個入口js文件,能夠輸出多個js文件,那麼html怎麼辦呢?我但願開發過程是這樣的,我在html中設置標題、SEO等信息,編寫HTML內容代碼,webpack把相關的js文件自動插入到html底部就行,能夠的,這須要用到html-webpack-plugin 插件,能夠經過調用html模板文件打包最終html。git
安裝html-webpack-plugin
npm install --save-dev html-webpack-plugin
複製代碼
webpack.common.js中多入口配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports={
entry:[
index:'../src/page/index/index.js',
test:'../src/page/test/test.js'
],
output: {
filename: '[name].[hash].js', //輸出名稱後面跟哈希值,解決緩存問題
path: path.resolve(__dirname,'../dist')
},
....
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: '../src/page/index/index.html',
chunks: ['index'],
})
new HtmlWebpackPlugin({
filename: 'test.html',
template: '../src/page/test/test.html',
chunks: ['test'],
})
]
}
複製代碼
這樣設置存在一個問題,每次新增一個頁面,我就要到這裏添加一下,未免很麻煩,咱們其實能夠經過讀取 src/page下的js文件,自動加入入口配置;讀取 src/page下的全部html文件,自動調用new HtmlWebpackPlugin進行實例化。
讀取目錄下全部文件名,咱們須要引入glob,先安裝
npm install --save-dev glob
複製代碼
改進後的配置
const glob = require('glob')
const CleanWebpackPlugin = require('clean-webpack-plugin');
//多入口js的配置,讀取src/page下全部的js文件
function entries() {
let jsDir = path.resolve(__dirname, '../src/page')
let entryFiles = glob.sync(jsDir + '/**/*.js')
let map = {};
for (let i = 0; i < entryFiles.length; i++) {
let filePath = entryFiles[i];
let filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'));
map[filename] = filePath;
}
return map;
}
//讀取多個html模板,進行插件實例化
function newHtmlWebpackPlugins(){
let jsDir = path.resolve(__dirname, '../src/page')
let htmls = glob.sync(jsDir + '/**/*.html')
let plugins=[]
for (let i = 0; i < htmls.length; i++) {
let filePath = htmls[i];
let filename_no_extension = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'));
let filename=filename_no_extension.concat('.html')
plugins.push(new HtmlWebpackPlugin({
filename: filename,
template: filePath,
chunks: [filename_no_extension],
}))
}
return plugins
}
module.exports={
entry:entries(),
output: {
filename: '[name].[hash].js',
path: path.resolve(__dirname,'../dist')
},
....
plugins: [
...newHtmlWebpackPlugins()
]
}
複製代碼
好了,如今新增頁面不須要更改webpack配置了,只須要從新運行一下 npm run start便可
頭部導航和底部通欄咱們各個頁面都是同樣的,所以須要引入,那麼html中怎麼引入另外一個html呢,這須要用到raw-loader 或 html-withimg-loader
安裝raw-loader,raw-loader能夠加載文件原始內容(utf-8格式)
npm install --save-dev raw-loader
複製代碼
//目錄結構
+ src
+ html
+ tpl
- navbar.html //共用的頭部導航
- footer.html //共用的底部導航
+ page //頁面文件
+ index
- index.html
+ test
- test.html
複製代碼
咱們在index.html中能夠這麼引用導航和底部通欄
<!--引入通用的導航部分-->
<%=require('raw-loader!../../html/tpl/navbar.html')%>
<!--頁面的正式內容在這裏-->
<div id="app">
<p>首頁的內容在這裏</p>
</div>
<!--引入通用的底部欄-->
<%=require('raw-loader!../../html/tpl/footer.html')%>
複製代碼
最初我在查找解決方案的時候,看到文章推薦使用raw-loader,可是發現這樣存在一個問題,就是導航中沒法引用本地的圖片,好比導航中引用一個logo圖片,是找不到的,由於咱們打包的時候也會對圖片進行處理,後面添加hash值,直接寫圖片路徑是不行的,後來我改用 html-withimg-loader解決了
安裝html-withimg-loader,顧名思義,這個插件能夠加載帶有圖片的html
npm install --save-dev html-withimg-loader
複製代碼
<!--引入通用的導航部分-->
<%=require('html-withimg-loader!../../html/tpl/navbar.html')%>
<!--頁面的正式內容在這裏-->
<div id="app">
<p>首頁的內容在這裏</p>
</div>
<!--引入通用的底部欄-->
<%=require('html-withimg-loader!../../html/tpl/footer.html')%>
複製代碼
順便提一句,html中引用圖片地址是須要這樣寫的,須要經過require才行,簡單的填寫圖片地址是不行的
<img src="${require('../../img/react.png')}" width="50" height="50">
複製代碼
相信你們如今都已經學過ES6了,但是鑑於瀏覽器的兼容性,還無法爲所欲爲的用,須要插件支持,咱們首先安裝
npm install --save-dev babel-loader babel-core babel-preset-env
複製代碼
添加webpack配置
webpack.common.js,咱們只對src目錄下的js進行轉換
{
test: /\.js$/,
use: {
loader: 'babel-loader'
},
include: path.resolve(__dirname,'../src')
},
複製代碼
同時在項目目錄下添加一個名爲.babelrc的文件,對babel進行設置,支持佔有率大於1%的瀏覽器的最近2個版本
{
"presets": [
["env",{
"targets": {
"browsers": ["> 1%", "last 2 versions"]
}
}]
],
}
複製代碼
babel只是將ES6語法轉爲ES5的語法,好比箭頭函數轉爲function(){},可是對一些ES6特有的功能沒有轉換,好比new Map(),打包以後仍是new Map(),咱們還須要再安裝一個插件,完成這個轉換工做。
npm install --save-dev babel-plugin-transform-runtime
複製代碼
更改.babelrc文件
{
"presets": [
["env",{
"targets": {
"browsers": ["> 1%", "last 2 versions"]
}
}]
],
"plugins": ["transform-runtime"] //引入插件
}
複製代碼
如今能夠放心大膽的使用ES6了
首先仍是安裝相關插件
npm install --save-dev less less-loader css-loader style-loader
複製代碼
webpack.common.js配置
{
test: /\.css$/,
use:["style-loader","css-loader","less-loader"]
},
複製代碼
在index.js文件中,咱們就能夠這樣引入less文件了
import './index.less'
複製代碼
打包以後,運行html頁面,index.js會動態把css樣式插入到html頁面,這樣會形成一個問題,剛加載html的時候是一個樣式,js插入css樣式後是另外一個樣式,形成頁面閃爍一下,體驗很差(技術也要追求用戶體驗啊,不光是產品經理的事)。這有兩個解決方案吧,第一個就是在JS未加載完成以前,顯示一個loading動畫,把整個頁面遮蓋住,第二個就是把css文件路徑打包進html中,不要經過js動態添加,我選用的第二個方案。
咱們要把less文件打包到一個css文件中,須要用到插件extract-text-webpack-plugin
npm install --save-dev extract-text-webpack-plugin
複製代碼
webpack.common.js
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports={
rules: [
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader!less-loader"
})
},
]
}
plugins: [
new ExtractTextPlugin("[name].[hash:8].css"),
]
複製代碼
這樣打包以後的html中會引入css文件,相似這樣
<link href="index.5eb2501d.css" rel="stylesheet">
複製代碼
實際在我從0開始搭建的過程當中,是先進行webpack這塊的配置的,之因此放到最後是不想影響主幹內容,下面咱們也簡單介紹一下個人webpack配置。
webpack官方推薦不寫重複的配置,即把本地和生產環境共用的配置放到一個文件,而後經過merge進行合併
webpack.dev.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
const env=require("../config/dev.env")
module.exports=merge(common,{
mode:"development",
devtool: 'inline-source-map',
plugins:[
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new OpenBrowserPlugin({ url: 'http://localhost:5000' })
],
})
複製代碼
咱們能夠看到,經過webpack-merge插件,將共用配置webpack.common.js和開發的配置進行合併
new webpack.DefinePlugin({
'process.env': env
}),
複製代碼
DefinePlugin定義了全局變量process.env
new OpenBrowserPlugin({ url: 'http://localhost:5000' })
複製代碼
這個插件是爲了在咱們容許npm run start後,自動打開頁面http://localhost:5000,避免每次都手動打開。 webpack-dev-server 爲咱們提供了一個簡單的 web 服務器,而且可以實時從新加載,讓咱們能夠實時看到開發結果,關於web服務器的配置,我放到了dev-server.js中
const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.dev');
const options = {
contentBase: './dist',
hot: true,
host: 'localhost',
};
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);
server.listen(5000, 'localhost', () => {
console.log('dev server listening on port 5000');
});
複製代碼
在package.json中,咱們添加兩個腳本
"scripts": {
"start": "node webpack/dev-server.js",
"build": "npx webpack --config webpack/webpack.prod.js",
},
複製代碼
這樣咱們就能夠在命令行輸入兩個命令
npm run start :進入開發模式
npm run build:打包生產環境代碼
好了,基本上把我作的這個腳手架介紹完了,實際要理解還須要本身去試,看是一回事,作出來又是另外一回事,給別人講明白那就更不容易了,前端路漫漫,你們努力吧。
github地址:github.com/501351981/w…
這個腳手架還不完善,不過基本夠用了,後面我還會再作幾個腳手架,好比結合vue進行多頁面開發或移動端H5開發,有興趣能夠持續關注。