摘要:官方推荐不写重复的配置,即把本地和生产环境共用的配置放到一个文件,然后通过进行合并我们可以看到,通过插件,将共用配置和开发的配置进行合并定义了全局变量这个插件是为了在我们允许后,自动打开页面,避免每次都手动打开。
之前只知道webpack很强大,但是一直没有深入学习过,这次从头看了一下教程,然后从0开始搭建了一个多入口网站的开发脚手架,期间遇到过很多问题,所以有心整理一下,希望能给大家一点帮助。
多HTML网站使用webpack的必要性假如我们接到这样一个任务,开发一个简单的官网,比如只有十几个html页面。项目很简单,我们没有必要使用什么大型框架,但是如果只是传统的写几个html、js和css,肯定会遇到这几个问题:
网站导航和底部通栏是每个页面都共有的,如何实现复用?如果不复用,那么有改动的时候就要改n个页面,未免太傻
如何在更改后强制清空用户缓存?我们不可能要求用户手动去清除浏览器缓存,那样太傻
我想使用ES6进行js的开发,如何解决浏览器兼容性问题?
我想使用less进行css样式开发,如何转换?
可以看出,没有自动化打包工具的加入,这些问题我们是很难解决的,因此使用webpack势在必行。
要实现的目标看到这里,可能有的同学就急了,别废话,感觉进入正题吧,不!我们先定目标!无论做什么事情,都要先定目标,而不是干到哪里算哪里,这样是不会有大的提升的,正是在迈向目标的路上克服各种问题,我们才有进步,在进行这个脚手架搭建之前,我希望它是这样的
能够打包成多个html文件和js文件,即支持多入口
文件名称都要带上hash值,解决缓存问题
能够复用网站的头部导航栏和底部通栏
通过采用less进行样式的编写
能够支持ES6开发
用起来要方便,增加页面不需要手动去更改webpack的入口设置,希望能够根据目录下的文件自动配置
不希望通过js动态插入css样式,这样会造成页面闪烁,希望html中直接引入css地址,就像平时开发那样
能够实时看到开发的效果
build能够对代码进行压缩
好了,目标定了,开工
目录结构别急,我们先来捋一捋目录,别着急写代码,一个好的目录,能让我们思路清晰,我的目录结构如下
+ 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地址。
webpack目录存放webpack的配置文件,其中开发和生产通用配置 放到webpack.common.js中,开发特有配置放到webpack.dev.js中,生产特有配置放到webpack.prod.js中。
src是我们开发的主目录,其中page目录放置我们的页面文件,这里可能和平时有所不同,我把每个页面用到的html、js和less文件放到了一起,有的同学可能把所有html放到一个目录下,js放到一个目录下,但是这样存在一个问题,每次改动页面,都要去翻目录,非常的不方便,我们应该把这种高度相关的文件放到一起,而提取的各种css组件或js组件可以和页面分开放置。
github地址:https://github.com/501351981/...
多入口配置webpack支持多入口,即给定多个入口js文件,可以输出多个js文件,那么html怎么办呢?我希望开发过程是这样的,我在html中设置标题、SEO等信息,编写HTML内容代码,webpack把相关的js文件自动插入到html底部就行,可以的,这需要用到html-webpack-plugin 插件,可以通过调用html模板文件打包最终html。
安装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")%><%=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")%><%=require("html-withimg-loader!../../html/tpl/footer.html")%>首页的内容在这里
顺便提一句,html中引用图片地址是需要这样写的,需要通过require才行,简单的填写图片地址是不行的
支持ES6编写js相信大家现在都已经学过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了
使用Less编写样式首先还是安装相关插件
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文件,类似这样
webpack配置
实际在我从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地址:https://github.com/501351981/...
这个脚手架还不完善,不过基本够用了,后面我还会再做几个脚手架,比如结合vue进行多页面开发或移动端H5开发,有兴趣可以持续关注。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/53294.html
摘要:官方推荐不写重复的配置,即把本地和生产环境共用的配置放到一个文件,然后通过进行合并我们可以看到,通过插件,将共用配置和开发的配置进行合并定义了全局变量这个插件是为了在我们允许后,自动打开页面,避免每次都手动打开。 之前只知道webpack很强大,但是一直没有深入学习过,这次从头看了一下教程,然后从0开始搭建了一个多入口网站的开发脚手架,期间遇到过很多问题,所以有心整理一下,希望能给大家...
摘要:开箱即用的多页面脚手架基于模块化开发可复用的现代化网站感兴趣的朋友,请点个及时关注项目更新请点个项目请提特性支持前后端分离开发配置完整的打包方案支持本地开发热更新集成代码风格校验支持编写源码,编译生成生产代码内置开发环境,自动加样式前缀自 Webpack-seed 开箱即用的多页面脚手架, 基于webpack4.2x babel7.1x模块化开发可复用的现代化网站(Without Vu...
摘要:侧边栏我们先图解一下侧边栏的结构整个侧边栏主从上到下按区块分别放置了账号和若干功能项分割线的列表,很容易想到使用布局控件。账号信息区域中有账号头像粉丝头像账号文字信息和背景图,这块我们可以使用控件库的控件实现。 经过2周的学习,看过笔记1-8的小伙伴们已经有不少开始自己写APP了,我也按耐不住这股热情,想要自己开发个APP玩玩,so,从本篇起,仿造一个APP,项目从0开始,每篇增加一些...
摘要:五一之前就想写一篇关于的文章结果朋友结婚就不了了之了。记得最后一定要看注意事项更新仓库介绍官网类似一个极简的静态网站生成器用来写技术文档不能在爽。当然搭建成博客也不成问题。构建与自动部署用的或者的都可以也可以搭建在自己的服务器上。 五一之前就想写一篇关于Vuepress的文章,结果朋友结婚就不了了之了。记得最后一定要看注意事项! 更新:coding仓库:https://git.dev...
摘要:故九万里,则风斯在下矣,而后乃今培风背负青天,而莫之夭阏者,而后乃今将图南。这里是文件的内容部分最后更新时间最后更新时间默认不开启,它是基于提交的时间戳,所以我们的静态站点是需要通过的仓库进行管理的,并且它是按的时间来计算的。VuePress(0.x版本) 本blog配套了一个基于 VuePress 的一个简单的配置案例,此配置案例包含了本blog绝大部分配置案例,更多详情请移步这里VueP...
阅读 3383·2021-11-25 09:43
阅读 3446·2021-11-19 09:40
阅读 2384·2021-10-14 09:48
阅读 1259·2021-09-09 11:39
阅读 1902·2019-08-30 15:54
阅读 2800·2019-08-30 15:44
阅读 1977·2019-08-29 13:12
阅读 1515·2019-08-29 12:59