zoukankan      html  css  js  c++  java
  • 用vue构建多页面应用

    最近一直在研究使用vue做出来一些东西,但都是SPA的单页面应用,但实际工作中,单页面并不一定符合业务需求,所以这篇我就来说说怎么开发多页面的Vue应用,以及在这个过程会遇到的问题。

    准备工作

    在本地用vue-cli新建一个项目,这个步骤vue的官网上有,我就不再说了。

    这里有一个地方需要改一下,在执行npm install命令之前,在package.json里添加一个依赖,后面会用到。

    修改webpack配置

    这里展示一下我的项目目录

     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文件下,分别是:

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

    我就按照顺序放出完整的文件内容,然后在做修改或添加的位置用注释符标注出来:

    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的配置就结束了。

    但是还没完啦,下面继续。

    文件结构

     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文件,js文件和html文件。这三个文件的作用就相当于做spa单页面应用时,根目录的index.html页面模板,src文件下的main.jsapp.vue的功能。

    原先,入口文件只有一个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 试试看

  • 相关阅读:
    【Robot Framework】List 的相关使用方法
    robot framework ——关键字run keyword if 如何在一个条件下接多个执行语句,以及如何写复杂条件句-关键字Run Keywords和and
    Robotframework之页面元素操作功能
    selenium之 下拉选择框Select
    selenium修改readonly属性的元件
    Robotframework之Python的自定义库
    python 元类详解
    从<<JavaScript权威指南>>抄下来的一个例子
    链接
    Python+selenium之获取文本值和下拉框选择数据
  • 原文地址:https://www.cnblogs.com/ljx20180807/p/9760448.html
Copyright © 2011-2022 走看看