简单vue项目脚手架(vue+webpack2.0+vuex+vue-router)
github地址(内含简单例子)
使用技术栈
webpack(^2.6.1)
webpack-dev-server(^2.4.5)
vue(^2.3.3)
vuex(^2.3.1)
vue-router(^2.5.3)
vue-loader(^12.2.1)
eslint(^3.19.0)
需要学习的知识
vue.js
vuex
vue-router
vue-loader
webpack2
eslint
内容相当多,尤其是webpack2教程,官方脚手架vue-cli虽然相当完整齐全,但是修改起来还是挺花时间,于是自己参照网上的资料和之前做过的项目用到的构建工具地去写了一个简单vue项目脚手架。适用于多页面spa模式的业务场景(每个模块都是一个spa)。比较简单,主要就是一个webpack.config.js文件,没有说特意地去划分成分webpack.dev.config.js、webpack.prov.config.js等等。下面是整个webpack.config.js文件代码:
const { resolve } = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const glob = require('glob') module.exports = (options = {}) => {
// 配置文件,根据 run script不同的config参数来调用不同config
const config = require('./config/' + (process.env.npm_config_config || options.config || 'dev'))
// 遍历入口文件,这里入口文件与模板文件名字保持一致,保证能同时合成HtmlWebpackPlugin数组和入口文件数组
const entries = glob.sync('./src/modules/*.js')
const entryJsList = {}
const entryHtmlList = []
for (const path of entries) {
const chunkName = path.slice('./src/modules/'.length, -'.js'.length)
entryJsList[chunkName] = path
entryHtmlList.push(new HtmlWebpackPlugin({
template: path.replace('.js', '.html'),
filename: 'modules/' + chunkName + '.html',
chunks: ['manifest', 'vendor', chunkName]
}))
}
// 处理开发环境和生产环境ExtractTextPlugin的使用情况
function cssLoaders(loader, opt) {
const loaders = loader.split('!')
const opts = opt || {}
if (options.dev) {
if (opts.extract) {
return loader
} else {
return loaders
}
} else {
const fallbackLoader = loaders.shift()
return ExtractTextPlugin.extract({
use: loaders,
fallback: fallbackLoader
})
}
} const webpackObj = {
entry: Object.assign({
vendor: ['vue', 'vuex', 'vue-router']
}, entryJsList),
// 文件内容生成哈希值chunkhash,使用hash会更新所有文件
output: {
path: resolve(__dirname, 'dist'),
filename: options.dev ? 'static/js/[name].js' : 'static/js/[name].[chunkhash].js',
chunkFilename: 'static/js/[id].[chunkhash].js',
publicPath: config.publicPath
}, externals: { }, module: {
rules: [
// 只 lint 本地 *.vue 文件,需要安装eslint-plugin-html,并配置eslintConfig(package.json)
{
enforce: 'pre',
test: /.vue$/,
loader: 'eslint-loader',
exclude: /node_modules/
},
/*
http://blog.guowenfh.com/2016/08/07/ESLint-Rules/
http://eslint.cn/docs/user-guide/configuring
[eslint资料]
*/
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
// 需要安装vue-template-compiler,不然编译报错
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
sass: cssLoaders('vue-style-loader!css-loader!sass-loader', { extract: true })
}
}
},
{
// 需要有相应的css-loader,因为第三方库可能会有文件
// (如:element-ui) css在node_moudle
// 生产环境才需要code抽离,不然的话,会使热重载失效
test: /\.css$/,
use: cssLoaders('style-loader!css-loader')
},
{
test: /\.(scss|sass)$/,
use: cssLoaders('style-loader!css-loader!sass-loader')
},
{
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
name: 'static/imgs/[name].[ext]?[hash]'
}
}
]
}
]
}, plugins: [
...entryHtmlList,
// 抽离css
new ExtractTextPlugin({
filename: 'static/css/[name].[chunkhash].css',
allChunks: true
}),
// 抽离公共代码
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'manifest']
}),
// 定义全局常量
// cli命令行使用process.env.NODE_ENV不如期望效果,使用不了,所以需要使用DefinePlugin插件定义,定义形式'"development"'或JSON.stringify('development')
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: options.dev ? JSON.stringify('development') : JSON.stringify('production')
}
}) ], resolve: {
// require时省略的扩展名,不再需要强制转入一个空字符串,如:require('module') 不需要module.js
extensions: ['.js', '.json', '.vue', '.scss', '.css'],
// require路径简化
alias: {
'~': resolve(__dirname, 'src'),
// Vue 最早会打包生成三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。
// vue.js = vue.common.js + compiler.js,默认package.json的main是指向vue.common.js,而template 属性的使用一定要用compiler.js,因此需要在alias改变vue指向
vue: 'vue/dist/vue'
},
// 指定import从哪个目录开始查找
modules: [
resolve(__dirname, 'src'),
'node_modules'
]
},
// 开启http服务,publicPath => 需要与Output保持一致 || proxy => 反向代理 || port => 端口号
devServer: config.devServer ? {
port: config.devServer.port,
proxy: config.devServer.proxy,
publicPath: config.publicPath,
stats: { colors: true }
} : undefined,
// 屏蔽文件超过限制大小的warn
performance: {
hints: options.dev ? false : 'warning'
},
// 生成devtool,保证在浏览器可以看到源代码,生产环境设为false
devtool: 'inline-source-map'
} if (!options.dev) {
webpackObj.devtool = false
webpackObj.plugins = (webpackObj.plugins || []).concat([
// 压缩js
new webpack.optimize.UglifyJsPlugin({
// webpack2,默认为true,可以不用设置
compress: {
warnings: false
}
}),
// 压缩 loaders
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
} return webpackObj
}
上面的代码对于每个配置项都有注释说明,这里有几点需要注意的:
1. webpack.config.js导出的是一个function
之前项目的webpack.config.js是以对象形式export的,如下
module.exports = {
entry: ...,
output: {
...
},
...
}
而现在倒出来的是一个function,如下:
module.exports = (options = {}) => {
return {
entry: ...,
output: {
...
},
...
}
}
这样的话,function会在执行webpack CLI的时候获取webpack的参数,通过options传进function,看一下package.json:
"local": "npm run dev --config=local",
"dev": "webpack-dev-server -d --hot --inline --env.dev --env.config dev",
"build": "rimraf dist && webpack -p --env.config prod" //rimraf清空dist目录
对于local
命令,我们执行的是dev
命令,但是在最后面会--config=local
,这是配置,这样我们可以通过process.env.npm_config_config
获取到,而对于dev
命令,对于--env XXX
,我们便可以在function获取option.config
= 'dev' 和 option.dev
= true的值,特别方便!以此便可以同步参数来加载不同的配置文件了。对于-d
、-p
不清楚的话,可以这里查看,很详细!
// 配置文件,根据 run script不同的config参数来调用不同config
const config = require('./config/' + (process.env.npm_config_config || options.config || 'dev'))
2. modules放置模板文件、入口文件、对应模块的vue文件
将入口文件和模板文件放到modules目录(名字保持一致),webpack文件会通过glob读取modules目录,遍历生成入口文件对象和模板文件数组,如下:
const entries = glob.sync('./src/modules/*.js')
const entryJsList = {}
const entryHtmlList = []
for (const path of entries) {
const chunkName = path.slice('./src/modules/'.length, -'.js'.length)
entryJsList[chunkName] = path
entryHtmlList.push(new HtmlWebpackPlugin({
template: path.replace('.js', '.html'),
filename: 'modules/' + chunkName + '.html',
chunks: ['manifest', 'vendor', chunkName]
}))
}
对于HtmlWebpackPlugin插件中几个配置项的意思是,template:模板路径,filename:文件名称,这里为了区分开来模板文件我是放置在dist/modules文件夹中,而对应的编译打包好的js、img(对于图片我们是使用file-loader、url-loader进行抽离,对于这两个不是很理解的,可以看这里)、css我也是会放在dist/下对应目录的,这样目录会比较清晰。chunks:指定插入文件中的chunk,后面我们会生成manifest文件、公共vendor、以及对应生成的jscss(名称一样)
3. 处理开发环境和生产环境ExtractTextPlugin的使用情况
开发环境,不需要把css进行抽离,要以style插入html文件中,可以很好实现热替换 生产环境,需要把css进行抽离合并,如下(根据options.dev区分开发和生产):
// 处理开发环境和生产环境ExtractTextPlugin的使用情况
function cssLoaders(loader, opt) {
const loaders = loader.split('!')
const opts = opt || {}
if (options.dev) {
if (opts.extract) {
return loader
} else {
return loaders
}
} else {
const fallbackLoader = loaders.shift()
return ExtractTextPlugin.extract({
use: loaders,
fallback: fallbackLoader
})
}
}
...
// 使用情况
// 注意:需要安装vue-template-compiler,不然编译会报错
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
sass: cssLoaders('vue-style-loader!css-loader!sass-loader', { extract: true })
}
}
},
...
{
test: /\.(scss|sass)$/,
use: cssLoaders('style-loader!css-loader!sass-loader')
}
再使用ExtractTextPlugin合并抽离到static/css/
目录
4. 定义全局常量
cli命令行(webpack -p
)使用process.env.NODE_ENV不如期望效果,使用不了,所以需要使用DefinePlugin插件定义,定义形式'"development"'或JSON.stringify(process.env.NODE_ENV),我使用这样的写法'development',结果报错(针对webpack2),查找了一下网上资料,它是这样讲的,可以去看一下,设置如下:
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: options.dev ? JSON.stringify('development') : JSON.stringify('production')
}
})
5. 使用eslint修正代码规范
通过eslint来检查代码的规范性,通过定义一套配置项,来规范代码,这样多人协作,写出来的代码也会比较优雅,不好的地方是,就是配置项太多,有些默认项设置我们不需要,但是确是处处限制我们,需要通过配置屏蔽掉,可以通过.eslintrc
文件或是package.json的eslintConfig
,还有其他方式,可以到中文网看,这里我用的是package.json方式,如下:
...
"eslintConfig": {
"parser": "babel-eslint",
"extends": "enough",
"env": {
"browser": true,
"node": true,
"commonjs": true,
"es6": true
},
"rules": {
"linebreak-style": 0,
"indent": [2, 4],
"no-unused-vars": 0,
"no-console": 0
},
"plugins": [
"html"
]
},
...
我们还需要安装 npm install eslint eslint-config-enough eslint-loader --save-dev
,eslint-config-enough是所谓的配置文件,这样package.json的内容才能起效,但是不当当是这样,对应编辑器也需要安装对应的插件,sublime text 3需要安装SublimeLinter、SublimeLinter-contrib-eslint插件。对于所有规则的详解,可以去看官网,也可以去这里看,很详细!
由于我们使用的是vue-loader,自然我们是希望能对.vue文件eslint,那么需要安装eslint-plugin-html,在package.json中进行配置。然后对应webpack配置:
{
enforce: 'pre',
test: /.vue$/,
loader: 'eslint-loader',
exclude: /node_modules/
}
我们会发现webpack v1和v2之间会有一些不同,比如webpack1对于预先加载器处理的执行是这样的,
module: {
preLoaders: [
{
test: /\.js$/,
loader: "eslint-loader"
}
]
}
更多的不同可以到中文网看,很详细,不做拓展。
6. alias vue指向问题
...
alias: {
vue: 'vue/dist/vue'
},
...
Vue 最早会打包生成三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。vue.js = vue.common.js + compiler.js,默认package.json的main是指向vue.common.js,而template 属性的使用一定要用compiler.js,因此需要在alias改变vue指向
7. devServer的使用
之前的项目中使用的是用express启动http服务,webpack-dev-middleware+webpack-hot-middleware,这里会用到compiler+compilation,这个是webpack的编译器和编译过程的一些知识,也不是很懂,后续要去做做功课,应该可以加深对webpack运行机制的理解。这样做的话,感觉复杂很多,对于webpack2.0 devServer似乎功能更强大更加完善了,所以直接使用就可以了。如下:
devServer: {
port: 8080, //端口号
proxy: { //方向代理 /api/auth/ => http://api.example.dev
'/api/auth/': {
target: 'http://api.example.dev',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
},
publicPath: config.publicPath,
stats: { colors: true }
}
//changeOrigin会修改HTTP请求头中的Host为target的域名, 这里会被改为api.example.dev
//pathRewrite用来改写URL, 这里我们把/api前缀去掉,直接使用/auth/请求
webpack 2 打包实战讲解得非常好,非常棒。可以去看一下,一定会有所收获!
8. 热重载原理
webpack中文网,讲的还算清楚,不过可能太笨,看起来还是云里雾里的,似懂非懂的,补补课,好好看看。
9. localtunnel的使用
Localtunnel 是一个可以让内网服务器暴露到公网上的开源项目,使用可以看这里,
$ npm install -g localtunnel
$ lt --port 8080
your url is: https://uhhzexcifv.localtunnel.me
这样的话,可以把我们的本地网站暂时性地暴露到公网,可以对网站做一些线上线下对比,详细内容可以去了解一下localtunnel,这里讲的是通过上面配置,访问https://uhhzexcifv.localtunnel.me
,没有达到理想效果,出现了Invalid Host header
的错误,因为devServer缺少一个配置disableHostCheck: true
,这样的一个配置,很多文档上面都没有说明,字面上面的意思不要去检查Host
,这样设置,便可以绕过这一层检验,设置的配置项在optionsSchema.json中,issue可以看这里
10. 升级webpack3.0
webpack3.0完美向下兼容,添加了些新特性,如范围提升,魔法注释 ”Magic Comments(暂时不知道怎么用),升级过程遇到Uncaught TypeError: Cannot read property 'call' of undefined
的错误,最后在HtmlWebpackPlugin插件配置了chunksSortMode: 'dependency'
解决了。
文章内容可能会更新,可以关注github
简单vue项目脚手架(vue+webpack2.0+vuex+vue-router)的更多相关文章
- 简单vue项目脚手架
简单vue项目脚手架 github地址 使用技术栈 webpack(^2.6.1) webpack-dev-server(^2.4.5) vue(^2.3.3) vuex(^2.3.1) vue-ro ...
- 分享一个vue项目“脚手架”项目的实现步骤
搭建缘由 源于公司每次新启动一个由多人协同开发的项目都由负责人初始化项目之后,每个人再去从私服pull一下项目才开始开发.但是每次初始化工程都是一步步的造轮子,一个个依赖去安装,新建一个个不同功能的文 ...
- vue项目中,main.js,App.vue,index.html如何调用
1.main.js是我们的入口文件,主要作用是初始化vue实例,并引入所需要的插件 2.App.vue是我们的主组件,所有页面都是在App.vue下进行切换的.其实你也可以理解为所有的路由也是App. ...
- vue项目在IE下报 [vuex] vuex requires a Promise polyfill in this browser错误
ie浏览器下报错 vue刚搭建的项目,在谷歌浏览器能够正常访问,但是在ie11等ie浏览器下无法显示页面,打开控制台查看无报错信息,打开仿真一栏,提示[vuex] vuex requires a Pr ...
- 优雅的写好Vue项目代码 — 路由拆分、Vuex模块拆分、element按需加载
目录 路由的拆分 VUEX模块拆分 Element UI库按需加载的优雅写法 路由的拆分 项目较大路由较多时,路由拆分是一个不错的代码优化方案,按不同业务分为多个模块,结构清晰便于统一管理. requ ...
- vue项目在IE下报 [vuex] vuex requires a Promise polyfill in this browser问题
如下图所示,项目在IE11下打开报错: 因为使用了 ES6 中用来传递异步消息的的Promise,而IE浏览器都不支持. 解决方法: 第一步: 安装 babel-polyfill . babel-po ...
- Vue 项目中 外部js 如何获取 vue 实例
1.将main.js 中的 Vue 实例暴露出去 2.在外部js中导入main.js ( import vm from '../main' );
- 使用vue-cli脚手架初始化Vue项目下的项目结构
概述 vue-cli是Vue 提供的一个官方命令行工具,可用于快速搭建大型单页应用.该工具提供开箱即用的构建工具配置,带来现代化的前端开发流程.只需几分钟即可创建并启动一个带热重载.保存时静态检查以及 ...
- vue项目里面使用脚手架实现跨域
今天在做vue项目的时候,项目在本地,接口数据在阿里云,这就造成了跨域,在网上找了好久,网上大部分的方法都是找到config文件夹下面的index进行修改的,可是我找到的Index却和他们描述的不一样 ...
随机推荐
- mvc 筛选器
之前公司中,运用ActionFilterAttribute特性实现用户登录信息的验证,没事看了看,留下点东西备忘. 好的,瞅这玩意一眼就大概能猜到这货是干嘛的了吧,没错,action过滤器.其实就是A ...
- HTML、CSS、JS 样式
把一个数组(一维或二维等)的内容转化为对应的字符串.相当于把print_r($array)显示出来的内容赋值给一个变量.$data= array('hello',',','world','!'); $ ...
- Asp.NET MVC 之心跳/长连接
0x01 在线用户类,我的用户唯一性由ID和类型识别(因为在不同的表里) public class UserIdentity : IEqualityComparer<UserIdentity&g ...
- 查询表达式和LINQ to Objects
查询表达式实际上是由编译器“预处理”为“普通”的C#代码,接着以完全普通的方式进行编译.这种巧妙的发式将查询集合到了语言中,而无须把语义改得乱七八糟 LINQ的介绍 LINQ中的基础概念 降低两种数据 ...
- Android计时器 android.widget.Chronometer
说起做定时器,大家一般会想到Timer和Executors的定时器线程池,其实用这两个做都会有问题,在停止和重新计时时你回发现无法停止或者说计时加快(加快是因为多个线程在记录同一个变量),Androi ...
- Docker- 创建支持SSH服务的容器镜像
示例 - CentOS7 [root@CentOS-7 ~]# cat ssh-centos7 FROM centos:centos7 MAINTAINER anliven "anliven ...
- silverlight WPF 水纹文本框
silverlight3取消了watertextbox控件,只有自己实现了个,实现了和textbox一样的无差异使用,只需要设置defaulttext就可以了 using System; using ...
- [笔记]ACM笔记 - 排序小技巧
Description 一个数组,要求先对前n个数字排序(以方便后续操作):又要求对前n+i个数字排序:又要求对前n+j - 前n+k个数字排序(i.j.k的大小远小于n,且i.j.k间没有大小关系) ...
- VR全景智慧城市——“海市蜃楼”般的逛街体验
<史记·天官书>:"海旁蜃气像楼台:广野气成宫阙然." 海市蜃楼,简称蜃景,是一种因为光的折射和全反射而形成的自然现象,是地球上物体反射的光经大气折射而形成的虚像. 2 ...
- opencv探索之路(十二):感兴趣区域ROI和logo添加技术
在图像处理领域,有一个非常重要的名词ROI. 什么是ROI? 它的英文全称是Region Of Interest,对应的中文解释就是感兴趣区域. 感兴趣区域,就是我们从图像中选择一个图像区域,这个区域 ...