简单vue项目脚手架

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可以看这里

简单vue项目脚手架的更多相关文章

  1. 简单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 ...

  2. 分享一个vue项目“脚手架”项目的实现步骤

    搭建缘由 源于公司每次新启动一个由多人协同开发的项目都由负责人初始化项目之后,每个人再去从私服pull一下项目才开始开发.但是每次初始化工程都是一步步的造轮子,一个个依赖去安装,新建一个个不同功能的文 ...

  3. webpack工具学习 构建简单vue项目(不依赖vue-cli) webpack4.0

    目的用webpack构建简单前端项目 1.npm init   (npm init -y)  形成package.json 2.npm install --save-dev webpack  形成 n ...

  4. vue项目搭建及创建、目录结构、项目启动、全局配置

    Vue项目环境搭建 """ node ~~ python:node是用c++编写用来运行js代码的 npm(cnpm) ~~ pip:npm是一个终端应用商城,可以换国内 ...

  5. day66_10_10,vue项目环境搭建

    一.下载. 首先去官网查看网址. 下载vue环境之前需要先下载node,使用应用商城npm下载,可以将其下载源改成cnpm: """ node ~~ python:nod ...

  6. Vue框架(三)——Vue项目搭建和项目目录介绍、组件、路由

    Vue项目环境搭建 1) 安装node,在官网下载好,然后在本地安装 官网下载安装包,傻瓜式安装:https://nodejs.org/zh-cn/ 2) 换源安装cnpm >: npm ins ...

  7. Vue 项目环境搭建

    Vue项目环境搭建 ''' 1) 安装node 官网下载安装包,傻瓜式安装:https://nodejs.org/zh-cn/ 2) 换源安装cnpm >: npm install -g cnp ...

  8. 如何创建vue项目

    Vue项目环境搭建 """ node ~~ python:node是用c++编写用来运行js代码的 npm(cnpm) ~~ pip:npm是一个终端应用商城,可以换国内 ...

  9. vue项目配置及项目初识

    目录 Vue项目环境搭建 Vue项目创建 重构项目依赖 1.需要转移的文件 2.重构依赖 pycharm配置并启动vue项目 vue项目目录结构分析 vue组件(.vue文件) 全局脚本文件main. ...

随机推荐

  1. Write Combining Buffer

    现代CPU使用了很多技术来降低对内存存取数据的延时,因为CPU执行的速度实在是太快了,在从内存存取数据的约120ns中,可以执行数百条指令. 其中多级的缓存架构就是为了减少这种延时,来提高CPU的利用 ...

  2. Flutter 设计模式|工厂模式家族

    文/ 杨加康,CFUG 社区成员,<Flutter 开发之旅从南到北>作者,小米工程师 在围绕设计模式的话题中,工厂这个词频繁出现,从 简单工厂 模式到 工厂方法 模式,再到 抽象工厂 模 ...

  3. Linux CentOS7.X-目录切换

    一.cd 命令 在Linux中,进行目录的切换需要使用cd命令. 二.Linux目录结构  三.Linux CentOS7中几个常用目录切换方式 1.cd usr 切换到当前目录下的usr目录. 2. ...

  4. 面试官:Redis中集合数据类型的内部实现方式是什么?

    虽然已经是阳春三月,但骑着共享单车骑了这么远,还有有点冷的.我搓了搓的被冻的麻木的手,对着前台的小姐姐说:"您好,我是来面试的."小姐姐问:"您好,您叫什么名字?&quo ...

  5. Android 12(S) 图形显示系统 - BufferQueue/BLASTBufferQueue之初识(六)

    题外话 你有没有听见,心里有一声咆哮,那一声咆哮,它好像在说:我就是要从后面追上去! 写文章真的好痛苦,特别是自己对这方面的知识也一知半解就更加痛苦了.这已经是这个系列的第六篇了,很多次都想放弃了,但 ...

  6. docker 部署mysql tomcat时 在root@localhost~下——-——docker exec -it 容器名 /bin/bash 报错

    在docker部署mysql时 报错 找不到 容器不自动启动  , docker start 容器名  也没有效果 多次尝试发现 原因是创建容器时在 下面创建的 更改为下创建 问题解决

  7. Java基础——自动装箱与拆箱

    装箱:把基本数据类型转换为对应的包装类类型 //装箱Integer i=Integer.ValuesOf(100);//自动装箱integer i1=100;//能够直接赋值成功的称为自动装箱,也调用 ...

  8. centOS 7 离线安装 MySQL 5.6 完美安装

    centOS 7 离线安装 MySQL 5.6 centOS 7 离线安装 MySQL 5.6 准备环境 1.离线 centOS 7(此处为 centOS 7 最小安装) 2.nginx 安装文件 ( ...

  9. 选择Key-Value Store

    [IT168 专稿]在之前的文章中,给大家介绍了<Redis快速入门:Key-Value存储系统简介>,今天进一步给大家介绍为什么选择Key-Value Store.Key-Value S ...

  10. CF375E Red and Black Tree(线性规划)

    CF375E Red and Black Tree(线性规划) Luogu 题解时间 很明显有一个略显复杂的 $ n^3 $ dp,但不在今天讨论范围内. 考虑一些更简单的方法. 设有 $ m $ 个 ...