以前只知道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开发,有兴趣能够持续关注。