Home  >  Article  >  Web Front-end  >  Introduction to vue project scaffolding

Introduction to vue project scaffolding

零下一度
零下一度Original
2017-06-26 10:52:102242browse

github address (contains simple examples)

Use technology stack

  • 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)

Knowledge to learn

vue.js
vuex
vue -router
vue-loader
webpack2
eslint


There is quite a lot of content, especially the webpack2 tutorial. Although the official scaffolding vue-cli is quite complete, it is still quite complicated to modify. Time, so I wrote a simple Vue project scaffolding by referring to the information on the Internet and the construction tools used in previous projects. Suitable for business scenarios in multi-page spa mode (each module is a spa). It is relatively simple. It is mainly just a webpack.config.js file. It does not say that it is specially divided into components webpack.dev.config.js, webpack.prov.config.js, etc. The following is the entire webpack.config.js file code:

  1 const { resolve } = require('path')  2 const webpack = require('webpack')  3 const HtmlWebpackPlugin = require('html-webpack-plugin')  4 const ExtractTextPlugin = require('extract-text-webpack-plugin')  5 const glob = require('glob')  6   7 module.exports = (options = {}) => {  8     // 配置文件,根据 run script不同的config参数来调用不同config  9     const config = require('./config/' + (process.env.npm_config_config || options.config || 'dev')) 10     // 遍历入口文件,这里入口文件与模板文件名字保持一致,保证能同时合成HtmlWebpackPlugin数组和入口文件数组 11     const entries = glob.sync('./src/modules/*.js') 12     const entryJsList = {} 13     const entryHtmlList = [] 14     for (const path of entries) { 15         const chunkName = path.slice('./src/modules/'.length, -'.js'.length) 16         entryJsList[chunkName] = path 17         entryHtmlList.push(new HtmlWebpackPlugin({ 18             template: path.replace('.js', '.html'), 19             filename: 'modules/' + chunkName + '.html', 20             chunks: ['manifest', 'vendor', chunkName] 21         })) 22     } 23     // 处理开发环境和生产环境ExtractTextPlugin的使用情况 24     function cssLoaders(loader, opt) { 25         const loaders = loader.split('!') 26         const opts = opt || {} 27         if (options.dev) { 28             if (opts.extract) { 29                 return loader 30             } else { 31                 return loaders 32             } 33         } else { 34             const fallbackLoader = loaders.shift() 35             return ExtractTextPlugin.extract({ 36                 use: loaders, 37                 fallback: fallbackLoader 38             }) 39         } 40     } 41  42     const webpackObj = { 43         entry: Object.assign({ 44             vendor: ['vue', 'vuex', 'vue-router'] 45         }, entryJsList), 46         // 文件内容生成哈希值chunkhash,使用hash会更新所有文件 47         output: { 48             path: resolve(__dirname, 'dist'), 49             filename: options.dev ? 'static/js/[name].js' : 'static/js/[name].[chunkhash].js', 50             chunkFilename: 'static/js/[id].[chunkhash].js', 51             publicPath: config.publicPath 52         }, 53  54         externals: { 55  56         }, 57  58         module: { 59             rules: [ 60                 // 只 lint 本地 *.vue 文件,需要安装eslint-plugin-html,并配置eslintConfig(package.json) 61                 { 62                     enforce: 'pre', 63                     test: /.vue$/, 64                     loader: 'eslint-loader', 65                     exclude: /node_modules/ 66                 }, 67                 /* 68                      69                      70                     [eslint资料] 71                  */ 72                 { 73                     test: /\.js$/, 74                     exclude: /node_modules/, 75                     use: ['babel-loader', 'eslint-loader'] 76                 }, 77                 // 需要安装vue-template-compiler,不然编译报错 78                 { 79                     test: /\.vue$/, 80                     loader: 'vue-loader', 81                     options: { 82                         loaders: { 83                             sass: cssLoaders('vue-style-loader!css-loader!sass-loader', { extract: true }) 84                         } 85                     } 86                 }, 87                 { 88                     // 需要有相应的css-loader,因为第三方库可能会有文件 89                     // (如:element-ui) css在node_moudle 90                     // 生产环境才需要code抽离,不然的话,会使热重载失效 91                     test: /\.css$/, 92                     use: cssLoaders('style-loader!css-loader') 93                 }, 94                 { 95                     test: /\.(scss|sass)$/, 96                     use: cssLoaders('style-loader!css-loader!sass-loader') 97                 }, 98                 { 99                     test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,100                     use: [101                         {102                             loader: 'url-loader',103                             options: {104                                 limit: 10000,105                                 name: 'static/imgs/[name].[ext]?[hash]'106                             }107                         }108                     ]109                 }110             ]111         },112 113         plugins: [114             ...entryHtmlList,115             // 抽离css116             new ExtractTextPlugin({117                 filename: 'static/css/[name].[chunkhash].css',118                 allChunks: true119             }),120             // 抽离公共代码121             new webpack.optimize.CommonsChunkPlugin({122                 names: ['vendor', 'manifest']123             }),124             // 定义全局常量125             // cli命令行使用process.env.NODE_ENV不如期望效果,使用不了,所以需要使用DefinePlugin插件定义,定义形式'"development"'或JSON.stringify('development')126             new webpack.DefinePlugin({127                 'process.env': {128                     NODE_ENV: options.dev ? JSON.stringify('development') : JSON.stringify('production')129                 }130             })131 132         ],133 134         resolve: {135             // require时省略的扩展名,不再需要强制转入一个空字符串,如:require('module') 不需要module.js136             extensions: ['.js', '.json', '.vue', '.scss', '.css'],137             // require路径简化138             alias: {139                 '~': resolve(__dirname, 'src'),140                 // Vue 最早会打包生成三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。141                 // vue.js = vue.common.js + compiler.js,默认package.json的main是指向vue.common.js,而template 属性的使用一定要用compiler.js,因此需要在alias改变vue指向142                 vue: 'vue/dist/vue'143             },144             // 指定import从哪个目录开始查找145             modules: [146                 resolve(__dirname, 'src'),147                 'node_modules'148             ]149         },150         // 开启http服务,publicPath => 需要与Output保持一致 || proxy => 反向代理 || port => 端口号151         devServer: config.devServer ? {152             port: config.devServer.port,153             proxy: config.devServer.proxy,154             publicPath: config.publicPath,155             stats: { colors: true }156         } : undefined,157         // 屏蔽文件超过限制大小的warn158         performance: {159             hints: options.dev ? false : 'warning'160         },161         // 生成devtool,保证在浏览器可以看到源代码,生产环境设为false162         devtool: 'inline-source-map'163     }164 165     if (!options.dev) {166         webpackObj.devtool = false167         webpackObj.plugins = (webpackObj.plugins || []).concat([168             // 压缩js169             new webpack.optimize.UglifyJsPlugin({170                 // webpack2,默认为true,可以不用设置171                 compress: {172                     warnings: false173                 }174             }),175             //  压缩 loaders176             new webpack.LoaderOptionsPlugin({177                 minimize: true178             })179         ])180     }181 182     return webpackObj183 }

The above code has comments for each configuration item. There are a few points to note here. :

1. What webpack.config.js exports is a function

The webpack.config.js of the previous project was exported in object form, as follows

1 module.exports = {2     entry: ...,3     output: {4         ...5     },6     ...7 }

What is poured out now is a function, as follows:

1 module.exports = (options = {}) => { 
2     return {3         entry: ...,4         output: {5             ...6         },7         ...8     }9 }

In this case, the function will obtain the webpack parameters when executing the webpack CLI and pass them into the function through options. Take a look at package.json:

1     "local": "npm run dev --config=local",2     "dev": "webpack-dev-server -d --hot --inline --env.dev --env.config dev",3     "build": "rimraf dist && webpack -p --env.config prod" //rimraf清空dist目录

For the local command, we execute the dev command, but at the end there will be - -config=local, this is the configuration, so we can get it through process.env.npm_config_config, and for the dev command, for --env XXX, we can get the values ​​​​of option.config= 'dev' and option.dev= true in function, which is particularly convenient! In this way, parameters can be synchronized to load different configuration files. If you are not sure about -d, -p, you can check it here, it is very detailed!

1     // 配置文件,根据 run script不同的config参数来调用不同config2     const config = require('./config/' + (process.env.npm_config_config || options.config || 'dev'))

2. modules place the template file, entry file, and vue file of the corresponding module

Place the entry file and template file in the modules directory (the names must be consistent), The webpack file will read the modules directory through glob, traverse to generate the entry file object and template file array, as follows:

 1     const entries = glob.sync('./src/modules/*.js') 2     
 const entryJsList = {} 3     const entryHtmlList = [] 4     
 for (const path of entries) { 5         const chunkName = path.slice('./src/modules/'.length, -'.js'.length) 6         
 entryJsList[chunkName] = path 7         entryHtmlList.push(new HtmlWebpackPlugin({ 8             
 template: path.replace('.js', '.html'), 9             filename: 'modules/' + chunkName + '.html',10             
 chunks: ['manifest', 'vendor', chunkName]11         }))12     }

For the several configuration items in the HtmlWebpackPlugin plug-in, template: template Path, filename: file name. In order to distinguish the template files here, I place them in the dist/modules folder, and the corresponding compiled and packaged js and img (for images, we use file-loader and url-loader to extract Li, I don’t understand these two very well, you can see here), I will also put the css in the corresponding directory under dist/, so that the directory will be clearer. Chunks: Specify the chunk inserted into the file. Later we will generate the manifest file, public vendor, and corresponding generated jscss (with the same name)

3. Handle the usage of ExtractTextPlugin in the development environment and production environment

In the development environment, there is no need to extract the css. It needs to be inserted into the html file with style, which can achieve hot replacement. In the production environment, the css needs to be extracted and merged, as follows (distinguish between development and production according to options.dev):

 1     // 处理开发环境和生产环境ExtractTextPlugin的使用情况 2    
  function cssLoaders(loader, opt) { 3         const loaders = loader.split('!') 4         
  const opts = opt || {} 5         if (options.dev) { 6             if (opts.extract) { 7                 
  return loader 8             } else { 9                 return loaders10             }11         }
  else {12             const fallbackLoader = loaders.shift()13             
  return ExtractTextPlugin.extract({14                 use: loaders,15                 
  fallback: fallbackLoader16             })17         }18     }19     ...20     // 使用情况21    
   // 注意:需要安装vue-template-compiler,不然编译会报错22     {23         test: /\.vue$/,24         
   loader: 'vue-loader',25         options: {26             loaders: {27                 
   sass: cssLoaders('vue-style-loader!css-loader!sass-loader', { extract: true })28             }         
   }30     },31     ...32     {33         test: /\.(scss|sass)$/,34       
   use: cssLoaders('style-loader!css-loader!sass-loader')35     }

Use ExtractTextPlugin to merge and extract to the static/css/ directory

4. Define global constants

cli command Line (webpack -p) using process.env.NODE_ENV is not as good as expected and cannot be used, so you need to use the DefinePlugin plug-in definition, in the form of '"development"' or JSON.stringify(process.env.NODE_ENV) , I used the writing method 'development' like this, and the result was an error (for webpack2). I searched the online information and it said this. You can check it out. The settings are as follows:

1     new webpack.DefinePlugin({2         'process.env': {3            
 NODE_ENV: options.dev ? JSON.stringify('development') : JSON.stringify('production')4         
 }5    
 })

5. Use eslint to correct code specifications

Use eslint to check the standardization of the code, and standardize the code by defining a set of configuration items. In this way, when multiple people collaborate, the code written will be more elegant. The problem is that there are too many configuration items. We don’t need some of the default settings, but they do restrict us everywhere. They need to be blocked through configuration. You can use the .eslintrc file or the of package.json. eslintConfig, there are other ways, you can go to the Chinese website to see it, here I use the package.json method, as follows:

 1     ... 2   "eslintConfig": { 3     "parser": "babel-eslint", 4     "extends": "enough", 5    
  "env": { 6       "browser": true, 7       "node": true, 8       "commonjs": true, 9       "es6": true10     },
  11     "rules": {12       "linebreak-style": 0,13       "indent": [2, 4],14       "no-unused-vars": 0,15      
   "no-console": 016     },17     "plugins": [18       "html"19     ]20   },21   ...

我们还需要安装 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配置:

1     {2         enforce: 'pre',3         test: /.vue$/,4         loader: 'eslint-loader',5       
  exclude: /node_modules/6     }

我们会发现webpack v1和v2之间会有一些不同,比如webpack1对于预先加载器处理的执行是这样的,

1   module: {2     preLoaders: [3       {4         test: /\.js$/,5        
 loader: "eslint-loader"6       
}7     ]8   }

更多的不同可以到中文网看,很详细,不做拓展。

6. alias vue指向问题

1     ...2     alias: {3        
 vue: 'vue/dist/vue'4     },
 5     ...

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似乎功能更强大更加完善了,所以直接使用就可以了。如下:

 1     devServer: { 2         port: 8080, //端口号 3        
  proxy: { //方向代理 /api/auth/ => http://api.example.dev 4             '/api/auth/': { 5                 
  target: 'http://api.example.dev', 6                 changeOrigin: true, 7                 
  pathRewrite: { '^/api': '' } 8             } 9         },10         
  publicPath: config.publicPath,11         stats: { colors: true }12     }13     
  //changeOrigin会修改HTTP请求头中的Host为target的域名, 这里会被改为api.example.dev14     
  //pathRewrite用来改写URL, 这里我们把/api前缀去掉,直接使用/auth/请求

webpack 2 打包实战讲解得非常好,非常棒。可以去看一下,一定会有所收获!

8. 热重载原理

webpack中文网,讲的还算清楚,不过可能太笨,看起来还是云里雾里的,似懂非懂的,补补课,好好看看。

9. localtunnel的使用

Localtunnel 是一个可以让内网服务器暴露到公网上的开源项目,使用可以看这里,

1 $ npm install -g localtunnel2 $ lt --port 80803 your url is: https://uhhzexcifv.localtunnel.me

这样的话,可以把我们的本地网站暂时性地暴露到公网,可以对网站做一些线上线下对比,详细内容可以去了解一下localtunnel,这里讲的是通过上面配置,访问https://uhhzexcifv.localtunnel.me,没有达到理想效果,出现了Invalid Host header的错误,因为devServer缺少一个配置disableHostCheck: true,这样的一个配置,很多文档上面都没有说明,字面上面的意思不要去检查Host,这样设置,便可以绕过这一层检验,设置的配置项在optionsSchema.json中,issue可以看这里

文章内容可能会更新,可以关注github

The above is the detailed content of Introduction to vue project scaffolding. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn