用vue構建多頁面應用

最近一直在研究使用vue作出來一些東西,但都是SPA的單頁面應用,但實際工做中,單頁面並不必定符合業務需求,因此這篇我就來講說怎麼開發多頁面的Vue應用,以及在這個過程會遇到的問題。css

準備工做

在本地用vue-cli新建一個項目,這個步驟vue的官網上有,我就再也不說了。html

這裏有一個地方須要改一下,在執行npm install命令以前,在package.json裏添加一個依賴,後面會用到。vue

修改webpack配置

這裏展現一下個人項目目錄node

 1 ├── README.md
 2 ├── build
 3 │   ├── build.js
 4 │   ├── check-versions.js
 5 │   ├── dev-client.js
 6 │   ├── dev-server.js
 7 │   ├── utils.js
 8 │   ├── vue-loader.conf.js
 9 │   ├── webpack.base.conf.js
10 │   ├── webpack.dev.conf.js
11 │   └── webpack.prod.conf.js
12 ├── config
13 │   ├── dev.env.js
14 │   ├── index.js
15 │   └── prod.env.js
16 ├── package.json
17 ├── src
18 │   ├── assets
19 │   │   └── logo.png
20 │   ├── components
21 │   │   ├── Hello.vue
22 │   │   └── cell.vue
23 │   └── pages
24 │       ├── cell
25 │       │   ├── cell.html
26 │       │   ├── cell.js
27 │       │   └── cell.vue
28 │       └── index
29 │           ├── index.html
30 │           ├── index.js
31 │           ├── index.vue
32 │           └── router
33 │               └── index.js
34 └── static

 

在這一步裏咱們須要改動的文件都在build文件下,分別是:webpack

  • utils.js
  • webpack.base.conf.js
  • webpack.dev.conf.js
  • webpack.prod.conf.js

我就按照順序放出完整的文件內容,而後在作修改或添加的位置用註釋符標註出來:git

utils.js文件

  1 // utils.js文件
  2 
  3 var path = require('path')
  4 var config = require('../config')
  5 var ExtractTextPlugin = require('extract-text-webpack-plugin')
  6 
  7 exports.assetsPath = function (_path) {
  8     var assetsSubDirectory = process.env.NODE_ENV === 'production' ?
  9         config.build.assetsSubDirectory :
 10         config.dev.assetsSubDirectory
 11     return path.posix.join(assetsSubDirectory, _path)
 12 }
 13 
 14 exports.cssLoaders = function (options) {
 15     options = options || {}
 16 
 17     var cssLoader = {
 18         loader: 'css-loader',
 19         options: {
 20             minimize: process.env.NODE_ENV === 'production',
 21             sourceMap: options.sourceMap
 22         }
 23     }
 24 
 25     // generate loader string to be used with extract text plugin
 26     function generateLoaders(loader, loaderOptions) {
 27         var loaders = [cssLoader]
 28         if (loader) {
 29             loaders.push({
 30                 loader: loader + '-loader',
 31                 options: Object.assign({}, loaderOptions, {
 32                     sourceMap: options.sourceMap
 33                 })
 34             })
 35         }
 36 
 37         // Extract CSS when that option is specified
 38         // (which is the case during production build)
 39         if (options.extract) {
 40             return ExtractTextPlugin.extract({
 41                 use: loaders,
 42                 fallback: 'vue-style-loader'
 43             })
 44         } else {
 45             return ['vue-style-loader'].concat(loaders)
 46         }
 47     }
 48 
 49     // https://vue-loader.vuejs.org/en/configurations/extract-css.html
 50     return {
 51         css: generateLoaders(),
 52         postcss: generateLoaders(),
 53         less: generateLoaders('less'),
 54         sass: generateLoaders('sass', { indentedSyntax: true }),
 55         scss: generateLoaders('sass'),
 56         stylus: generateLoaders('stylus'),
 57         styl: generateLoaders('stylus')
 58     }
 59 }
 60 
 61 // Generate loaders for standalone style files (outside of .vue)
 62 exports.styleLoaders = function (options) {
 63     var output = []
 64     var loaders = exports.cssLoaders(options)
 65     for (var extension in loaders) {
 66         var loader = loaders[extension]
 67         output.push({
 68             test: new RegExp('\\.' + extension + '$'),
 69             use: loader
 70         })
 71     }
 72     return output
 73 }
 74 
 75 /* 這裏是添加的部分 ---------------------------- 開始 */
 76 
 77 // glob是webpack安裝時依賴的一個第三方模塊,還模塊容許你使用 *等符號, 例如lib/*.js就是獲取lib文件夾下的全部js後綴名的文件
 78 var glob = require('glob')
 79 // 頁面模板
 80 var HtmlWebpackPlugin = require('html-webpack-plugin')
 81 // 取得相應的頁面路徑,由於以前的配置,因此是src文件夾下的pages文件夾
 82 var PAGE_PATH = path.resolve(__dirname, '../src/pages')
 83 // 用於作相應的merge處理
 84 var merge = require('webpack-merge')
 85 
 86 
 87 //多入口配置
 88 // 經過glob模塊讀取pages文件夾下的全部對應文件夾下的js後綴文件,若是該文件存在
 89 // 那麼就做爲入口處理
 90 exports.entries = function () {
 91     var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
 92     var map = {}
 93     entryFiles.forEach((filePath) => {
 94         var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
 95         map[filename] = filePath
 96     })
 97     return map
 98 }
 99 
100 //多頁面輸出配置
101 // 與上面的多頁面入口配置相同,讀取pages文件夾下的對應的html後綴文件,而後放入數組中
102 exports.htmlPlugin = function () {
103     let entryHtml = glob.sync(PAGE_PATH + '/*/*.html')
104     let arr = []
105     entryHtml.forEach((filePath) => {
106         let filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
107         let conf = {
108             // 模板來源
109             template: filePath,
110             // 文件名稱
111             filename: filename + '.html',
112             // 頁面模板須要加對應的js腳本,若是不加這行則每一個頁面都會引入全部的js腳本
113             chunks: ['manifest', 'vendor', filename],
114             inject: true
115         }
116         if (process.env.NODE_ENV === 'production') {
117             conf = merge(conf, {
118                 minify: {
119                     removeComments: true,
120                     collapseWhitespace: true,
121                     removeAttributeQuotes: true
122                 },
123                 chunksSortMode: 'dependency'
124             })
125         }
126         arr.push(new HtmlWebpackPlugin(conf))
127     })
128     return arr
129 }
130 /* 這裏是添加的部分 ---------------------------- 結束 */

webpack.base.conf.js 文件

 1 // webpack.base.conf.js 文件
 2 
 3 var path = require('path')
 4 var utils = require('./utils')
 5 var config = require('../config')
 6 var vueLoaderConfig = require('./vue-loader.conf')
 7 
 8 function resolve(dir) {
 9   return path.join(__dirname, '..', dir)
10 }
11 
12 module.exports = {
13   /* 修改部分 ---------------- 開始 */
14   entry: utils.entries(),
15   /* 修改部分 ---------------- 結束 */
16   output: {
17     path: config.build.assetsRoot,
18     filename: '[name].js',
19     publicPath: process.env.NODE_ENV === 'production' ?
20       config.build.assetsPublicPath :
21       config.dev.assetsPublicPath
22   },
23   resolve: {
24     extensions: ['.js', '.vue', '.json'],
25     alias: {
26       'vue$': 'vue/dist/vue.esm.js',
27       '@': resolve('src'),
28       'pages': resolve('src/pages'),
29       'components': resolve('src/components')
30     }
31   },
32   module: {
33     rules: [{
34       test: /\.vue$/,
35       loader: 'vue-loader',
36       options: vueLoaderConfig
37     },
38     {
39       test: /\.js$/,
40       loader: 'babel-loader',
41       include: [resolve('src'), resolve('test')]
42     },
43     {
44       test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
45       loader: 'url-loader',
46       options: {
47         limit: 10000,
48         name: utils.assetsPath('img/[name].[hash:7].[ext]')
49       }
50     },
51     {
52       test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
53       loader: 'url-loader',
54       options: {
55         limit: 10000,
56         name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
57       }
58     }
59     ]
60   }
61 }

webpack.dev.conf.js 文件

  1 var utils = require('./utils')
  2 var webpack = require('webpack')
  3 var config = require('../config')
  4 var merge = require('webpack-merge')
  5 var baseWebpackConfig = require('./webpack.base.conf')
  6 var HtmlWebpackPlugin = require('html-webpack-plugin')
  7 var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
  8 
  9 // add hot-reload related code to entry chunks
 10 Object.keys(baseWebpackConfig.entry).forEach(function (name) {
 11   baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
 12 })
 13 
 14 module.exports = merge(baseWebpackConfig, {
 15   module: {
 16     rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
 17   },
 18   // cheap-module-eval-source-map is faster for development
 19   devtool: '#cheap-module-eval-source-map',
 20   plugins: [
 21     new webpack.DefinePlugin({
 22       'process.env': config.dev.env
 23     }),
 24     // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
 25     new webpack.HotModuleReplacementPlugin(),
 26     new webpack.NoEmitOnErrorsPlugin(),
 27     // https://github.com/ampedandwired/html-webpack-plugin
 28     /* 註釋這個區域的文件 ------------- 開始 */
 29     // new HtmlWebpackPlugin({
 30     //   filename: 'index.html',
 31     //   template: 'index.html',
 32     //   inject: true
 33     // }),
 34     /* 註釋這個區域的文件 ------------- 結束 */
 35     new FriendlyErrorsPlugin()
 36 
 37     /* 添加 .concat(utils.htmlPlugin()) ------------------ */
 38   ].concat(utils.htmlPlugin())
 39 })
 40 webpack.prod.conf.js 文件
 41 var path = require('path')
 42 var utils = require('./utils')
 43 var webpack = require('webpack')
 44 var config = require('../config')
 45 var merge = require('webpack-merge')
 46 var baseWebpackConfig = require('./webpack.base.conf')
 47 var CopyWebpackPlugin = require('copy-webpack-plugin')
 48 var HtmlWebpackPlugin = require('html-webpack-plugin')
 49 var ExtractTextPlugin = require('extract-text-webpack-plugin')
 50 var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
 51 
 52 var env = config.build.env
 53 
 54 var webpackConfig = merge(baseWebpackConfig, {
 55   module: {
 56     rules: utils.styleLoaders({
 57       sourceMap: config.build.productionSourceMap,
 58       extract: true
 59     })
 60   },
 61   devtool: config.build.productionSourceMap ? '#source-map' : false,
 62   output: {
 63     path: config.build.assetsRoot,
 64     filename: utils.assetsPath('js/[name].[chunkhash].js'),
 65     chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
 66   },
 67   plugins: [
 68     // http://vuejs.github.io/vue-loader/en/workflow/production.html
 69     new webpack.DefinePlugin({
 70       'process.env': env
 71     }),
 72     new webpack.optimize.UglifyJsPlugin({
 73       compress: {
 74         warnings: false
 75       },
 76       sourceMap: true
 77     }),
 78     // extract css into its own file
 79     new ExtractTextPlugin({
 80       filename: utils.assetsPath('css/[name].[contenthash].css')
 81     }),
 82     // Compress extracted CSS. We are using this plugin so that possible
 83     // duplicated CSS from different components can be deduped.
 84     new OptimizeCSSPlugin({
 85       cssProcessorOptions: {
 86         safe: true
 87       }
 88     }),
 89     // generate dist index.html with correct asset hash for caching.
 90     // you can customize output by editing /index.html
 91     // see https://github.com/ampedandwired/html-webpack-plugin
 92 
 93     /* 註釋這個區域的內容 ---------------------- 開始 */
 94     // new HtmlWebpackPlugin({
 95     //   filename: config.build.index,
 96     //   template: 'index.html',
 97     //   inject: true,
 98     //   minify: {
 99     //     removeComments: true,
100     //     collapseWhitespace: true,
101     //     removeAttributeQuotes: true
102     //     // more options:
103     //     // https://github.com/kangax/html-minifier#options-quick-reference
104     //   },
105     //   // necessary to consistently work with multiple chunks via CommonsChunkPlugin
106     //   chunksSortMode: 'dependency'
107     // }),
108     /* 註釋這個區域的內容 ---------------------- 結束 */
109 
110     // split vendor js into its own file
111     new webpack.optimize.CommonsChunkPlugin({
112       name: 'vendor',
113       minChunks: function (module, count) {
114         // any required modules inside node_modules are extracted to vendor
115         return (
116           module.resource &&
117           /\.js$/.test(module.resource) &&
118           module.resource.indexOf(
119             path.join(__dirname, '../node_modules')
120           ) === 0
121         )
122       }
123     }),
124     // extract webpack runtime and module manifest to its own file in order to
125     // prevent vendor hash from being updated whenever app bundle is updated
126     new webpack.optimize.CommonsChunkPlugin({
127       name: 'manifest',
128       chunks: ['vendor']
129     }),
130     // copy custom static assets
131     new CopyWebpackPlugin([{
132       from: path.resolve(__dirname, '../static'),
133       to: config.build.assetsSubDirectory,
134       ignore: ['.*']
135     }])
136     /* 該位置添加 .concat(utils.htmlPlugin()) ------------------- */
137   ].concat(utils.htmlPlugin())
138 })
139 
140 if (config.build.productionGzip) {
141   var CompressionWebpackPlugin = require('compression-webpack-plugin')
142 
143   webpackConfig.plugins.push(
144     new CompressionWebpackPlugin({
145       asset: '[path].gz[query]',
146       algorithm: 'gzip',
147       test: new RegExp(
148         '\\.(' +
149         config.build.productionGzipExtensions.join('|') +
150         ')$'
151       ),
152       threshold: 10240,
153       minRatio: 0.8
154     })
155   )
156 }
157 
158 if (config.build.bundleAnalyzerReport) {
159   var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
160   webpackConfig.plugins.push(new BundleAnalyzerPlugin())
161 }
162 
163 module.exports = webpackConfig

至此,webpack的配置就結束了。github

可是還沒完啦,下面繼續。web

文件結構

 1 ├── src
 2 │   ├── assets
 3 │   │   └── logo.png
 4 │   ├── components
 5 │   │   ├── Hello.vue
 6 │   │   └── cell.vue
 7 │   └── pages
 8 │       ├── cell
 9 │       │   ├── cell.html
10 │       │   ├── cell.js
11 │       │   └── cell.vue
12 │       └── index
13 │           ├── index.html
14 │           ├── index.js
15 │           ├── index.vue
16 │           └── router
17 │               └── index.js

src就是我所使用的工程文件了,assets,components,pages分別是靜態資源文件、組件文件、頁面文件。vue-cli

前兩個就很少說,主要是頁面文件裏,我目前是按照項目的模塊分的文件夾,你也能夠按照你本身的需求調整。而後在每一個模塊裏又有三個內容:vue文件,js文件和html文件。這三個文件的做用就至關於作spa單頁面應用時,根目錄的index.html頁面模板,src文件下的main.jsapp.vue的功能。npm

原先,入口文件只有一個main.js,但如今因爲是多頁面,所以入口頁面多了,我目前就是兩個:index和cell,以後若是打包,就會在dist文件下生成兩個HTML文件:index.htmlcell.html(能夠參考一下單頁面應用時,打包只會生成一個index.html,區別在這裏)。

cell文件下的三個文件,就是通常模式的配置,參考index的就能夠,但並不徹底相同。

特別注意的地方

cell.js

在這個文件裏,按照寫法,應該是這樣的吧:

1 import Vue from 'Vue'
2 import cell from './cell.vue'
3 
4 new Vue({
5     el:'#app',// 這裏參考cell.html和cell.vue的根節點id,保持三者一致
6     teleplate:'<cell/>',
7     components:{ cell }
8 })

 

這個配置在運行時(npm run dev)會報錯

1 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
2 (found in <Root>)

網上的解釋是這樣的:

運行時構建不包含模板編譯器,所以不支持 template 選項,只能用 render 選項,但即便使用運行時構建,在單文件組件中也依然能夠寫模板,由於單文件組件的模板會在構建時預編譯爲 render 函數。運行時構建比獨立構建要輕量30%,只有 17.14 Kb min+gzip大小。

上面一段是官方api中的解釋。就是說,若是咱們想使用template,咱們不能直接在客戶端使用npm install以後的vue。

也給出了相應的修改方案:

1 resolve: { alias: { 'vue': 'vue/dist/vue.js' } }

這裏是修改package.json的resolve下的vue的配置,不少人反應這樣修改以後就行了,可是我按照這個方法修改以後依然報錯。而後我就想到上面提到的render函數,所以個人修改是針對cell.js文件的。

1 import Vue from 'Vue'
2 import cell from './cell.vue'
3 
4 /* eslint-disable no-new */
5 new Vue({
6   el: '#app',
7   render: h => h(cell)
8 })

這裏面我用render函數取代了組件的寫法,在運行就沒問題了。

頁面跳轉

既然是多頁面,確定涉及頁面之間的互相跳轉,就按照我這個項目舉例,從index.html文件點擊a標籤跳轉到cell.html。

我最開始寫的是:

1  <!-- index.html -->
2 <a href='../cell/cell.html'></a>

但這樣寫,不管是在開發環境仍是最後測試,都會報404,找不到這個頁面。

改爲這樣既可:

1  <!-- index.html -->
2 <a href='cell.html'></a>

這樣他就會本身找cell.html這個文件。

打包後的資源路徑

執行npm run build以後,打開相應的html文件你是看不到任何東西的,查看緣由是找不到相應的js文件和css文件。

這時候的文件結構是這樣的:

1 ├── dist
2 │   ├── js
3 │   ├── css
4 │   ├── index.html
5 │   └── cell.html

查看index.html文件以後會發現資源的引用路徑是:

/dist/js.........

這樣,若是你的dist文件不是在根目錄下的,就根本找不到資源。

方法固然也有啦,若是你不嫌麻煩,就一個文件一個文件的修改路徑咯,或者像我同樣偷懶,修改config下的index.js文件。具體的作法是:

 1 build: {
 2     env: require('./prod.env'),
 3     index: path.resolve(__dirname, '../dist/index.html'),
 4     assetsRoot: path.resolve(__dirname, '../dist'),
 5     assetsSubDirectory: 'static',
 6     assetsPublicPath: '/',
 7     productionSourceMap: true,
 8     // Gzip off by default as many popular static hosts such as
 9     // Surge or Netlify already gzip all static assets for you.
10     // Before setting to `true`, make sure to:
11     // npm install --save-dev compression-webpack-plugin
12     productionGzip: false,
13     productionGzipExtensions: ['js', 'css'],
14     // Run the build command with an extra argument to
15     // View the bundle analyzer report after build finishes:
16     // `npm run build --report`
17     // Set to `true` or `false` to always turn it on or off
18     bundleAnalyzerReport: process.env.npm_config_report
19   },

將這裏面的

1 assetsPublicPath: '/',

改爲

1 assetsPublicPath: './',

以上內容就是實際項目運用的,這就能夠啦,在從新npm run build 試試看

相關文章
相關標籤/搜索