简单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. Qt:QMap

    0.说明 QMap < Key , T > 一个QMap就是一个K-V对,也可以说是字典对象. 1)构造 构造一个Key是QString,Value是int的QMap: QMap<Q ...

  2. c# 窗口不显示标题栏及周围有/无边框的设置

    一 : 不显示标题栏也无边框: 方法1:属性里直接设置 方法2:手写代码(两种效果一样) this.FormBorderStyle = FormBorderStyle.None; 二 : 不显示标题栏 ...

  3. JZ-055-链表中环的入口结点

    链表中环的入口结点 题目描述 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. 题目链接: 链表中环的入口结点 代码 import java.util.ArrayList; ...

  4. ws请求定时

    heartChechInit() {       const _this = this;       // 设置统筹管理       let heartCheck = {         timer: ...

  5. base64学习笔记

    关于是什么: 定义 8Bits字节编码方式之一 应用 传输8Bit字节代码 特性 Base64编码具有不可读性 描述 Base64可以成为密码学的基石,非常重要. 特点 可以将任意的二进制数据进行Ba ...

  6. php简易表单及下拉框动态渲染

    <?php//1.连接数据库$link = mysqli_connect('127.0.0.1','root','root','1906');//2.设置字符集mysqli_set_charse ...

  7. CF1453D题解

    VP 的时候发现的一道数学题( 在思考这个问题之前,先让我们思考一件事:走到距离上一个存档点 \(n\) 的位置的期望是多少?(假设这个值为 \(f[n]\)) 先思考 \(f[1]\) 是多少,很明 ...

  8. vue/cli项目添加外部js文件的一个方法

    有一个util.js文件,内容如下 function Util () { ... } export default new Util() 可以在main.js里面通过import引入js import ...

  9. Java 开发工具之Myeclipse快捷键

  10. MySQL SQL Injection Cheat Sheet

    MySQL SQL Injection Cheat Sheet Some useful syntax reminders for SQL Injection into MySQL databases- ...