美文网首页
手把手教你 vue-cli 单页到多页应用

手把手教你 vue-cli 单页到多页应用

作者: 何苦_python_java | 来源:发表于2018-06-05 19:47 被阅读0次

    我有一个cli创建的vue项目,但是我想做成多页应用,怎么办,废话不多说,直接开撸~

    约定:新增代码部分在//add和//end中间 删除(注释)代码部分在//del和//end中间,很
    多东西都写在注释里

    第一步:cli一个vue项目

    新建一个vue项目,官网:

    vue init webpack demo

    cli默认使用webpack的dev-server服务,这个服务是做不了单页的,需要手动建一个私服叫啥你随意 一般叫dev.server或者dev.client。

    第二步:添加两个方法处理出口入口文件(SPA默认写死的)

    进入刚刚创建vue项目:

    cd demo
    

    在目录下面找到 ·build/utils.js· 文件,修改部分 utils.js:

    'use strict'
    
    const path = require('path')
    const config = require('../config')
    const ExtractTextPlugin = require('extract-text-webpack-plugin')
    const packageConfig = require('../package.json')
    //add
    const glob = require('glob');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
       //功能:生成html文件及js文件并把js引入html
    const pagePath = path. resolve(__dirname, '../src/views/' );
      //页面的路径,比如这里我用的views,那么后面私服加入的文件监控器就会从src下面的views下面开始监控文件
    //end 
    exports. assetsPath = function(_path) {
        const assetsSubDirectory = process.env.NODE_ENV ==='production'
            ?config.build.assetsSubDirectory
            : config.dev.assetsSubDirectory
        return path posix.join(assetsSubDirectory,_path)
    }
    exports.cssLoaders = function (options) {
        options = options || {}
        const cssLoader = {
           loader: 'css-loader',
           options:  {
               sourceMap: options.sourceMap
          }
      }
    const postcssLoader = {
        loader: 'postcss-loader',
        options: {
             sourceMap: options.sourceMap
        }
    }
    // generate loader string to be used with extract text plugin
    function generateLoaders(loader, loaderOptions) {
        const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
        if (loader) {
           loaders.push({ 
              loader:  loader + '-loader',
              options: Object.assign({}, loaderOptions, {
                  sourceMap: options. sourceMap
                 })
          })
    }
      // Extract CSS when that option is specified
      // (which is the case during production build)
      if (options.extract) {
          return ExtractTextPlugin.extract({
              use: loaders,
              fallback: 'vue-style-loader'
          })
      } else {
           return [ 'vue-style-loader' ].concat(loaders)    
      }
    }
    // https://vue-loader.vuejs.org/en/configurations/extract-css.html
       return {
          css: generateLoaders(),
          postcss: generateLoaders(),
          less: generateLoaders('less'),
          sass: generateLoaders('sass', { indentedSyntax: true }), 
          scss: generateLoaders('sass'),
          stylus: generateLoaders('stylus'),
          styl: generateLoaders('stylus')
        }
    }
    // Generate loaders for standalone style files (outside of .vue)
    exports. styleLoaders = function (options) {
        const output = []
        const loaders = exports. cssLoaders(options)
        for (const extension in loaders) {
            const loader = loaders[extension]
            output. push({
                test: new RegExp('\\.' + extension + '$' ),
                use: loader
            })
      }
      return output
    }
    exports. createNotifierCallback = () => {
        const notifier = require('node-notifier')
        return ( severity, errors) => {
          if (severity !== 'error') return
          const error = errors[ 0 ]
          const filename = error. file && error. file. split('!').pop()
          notifier.notify({
             title: packageConfig.name,
             message: severity + ': ' + error.name,
             subtitle: filename || ' ',
             icon: path. join(__dirname, 'logo.png')
           })
        }
    }
    //add  新增一个方法处理入口文件(单页应用的入口都是写死,到时候替换成这个方法)
    exports. createEntry = () => {
       let files = glob. sync(pagePath + '/**/*.js');
       let entries = {};
       let basename;
       let foldername;
       files. forEach(entry => {
          // Filter the router.js
            basename = path. basename(entry, path. extname(entry), 'router.js');
            foldername = path.dirname(entry).split('/'). splice(-1)[0];
              // If foldername not equal basename, doing nothing
              // The folder maybe contain more js files, but only the same name is main
             if (basename === foldername) {
               entries[basename] = [
                    'webpack-hot-middleware/clientnoInfo=true&reload=true&path=/__webpack_hmr&timeout=20000',
                     entry];
             }
         });
        return entries;
    };
    //end
    //add 新增出口文
    exports. createHtmlWebpackPlugin = () => {
    let files = glob sync(pagePath + '/**/*.html', {matchBase: true});
    let entries = exports.createEntry();
    let plugins = [];
    let conf;
    let basename;
    let foldername;
    
    files.forEach(file => {
     basename = path. basename(file, path. extname(file));
     foldername = path. dirname(file). split('/'). splice(-1). join(' ');    
      if ( basename === foldername) {
          conf = { 
              template: file,
              filename: basename + '.html',
              inject: true,
              chunks: entries [basename] ? [basename]: []
             };
            if (process. env. NODE_ENV  !== 'development') {
                 conf. chunksSortMode = 'dependency' ;
                 conf minify = {removeComments: true,
                        collapseWhitespace: true,
                        removeAttributeQuotes: true
               };   
           }
           plugins. push(new HtmlWebpackPlugin(conf));
        }
      });
      return plugins;
    };
    //end
    
    第三步:创建私服(不使用dev-server服务,自己建一个)

    从express新建私服并配置(build文件夹下新建,我这里叫webpack.dev.client.js)webpack.dev.client.js:

    'use strict';
    const fs = require('fs');
    const path = require('path');
    const express = require('express');
    const webpack = require('webpack');
    const webpackDevMiddleware = require('webpack-dev-middleware');
       //文件监控(前面配置了从views下面监控)
    const webpackHotMiddleware = require('webpack-hot-middleware');
       //热加载
    const config = require('../config');
    const devWebpackConfig = require('./webpack.dev.conf');
    const proxyMiddleware = require('http-proxy-middleware');
       //跨域
    const proxyTable = config.dev.proxyTable;
    const PORT = config. dev. port;
    const HOST = config. dev. host;
    const assetsRoot = config. dev.assetsRoot;
    const app = express();
    const router = express. Router();
    const compiler = webpack(devWebpackConfig);
    let devMiddleware  = webpackDevMiddleware(compiler, {
       publicPath: devWebpackConfig. output. publicPath,
       quiet: true,
       stats: {
          colors: true,
          chunks: false
        }
    });
    let hotMiddleware = webpackHotMiddleware(compiler, {
      path: '/__webpack_hmr', 
      heartbeat: 2000
    });
    app.use(hotMiddleware);
    app.use(devMiddleware);
    Object.keys(proxyTable). forEach(function (context) {
        let  options = proxyTable[context];
        if (typeof options === 'string') {
           options = {
             target: options
           };
       }
        app. use(proxyMiddleware(context, options));
    });
    //双路由   私服一层控制私服路由    vue的路由控制该页面下的路由
    app. use(router)
    app. use('/static', express. static(path. join(assetsRoot, 'static')));
    let sendFile = (viewname, response, next) => { 
        compiler. outputFileSystem. readFile(viewname,(err, result) => {
            if(err) {
               return (next (err));
            }
            response.set('content-type', 'text/html');
            response.send(result);
            response. end();
          });
    };
    //拼接方法
    function pathJoin(patz) {
        return path.join(assetsRoot, patz);
    }
    /**
     * 定义路由(私服路由 非vue路由)
     * */
    // favicon
    router. get('/favicon.ico', (req, res, next) => {
        res.end();
    });
    // http://localhost:8080/
    router. get('/',(req, res, next)=>{
        sendFile(pathJoin ('index.html'), res, next);
    });
    // http://localhost:8080/home
    router. get('/:home',(req, res, next) => {
        sendFile( pathJoin ( req. params. home + '.html'), res, next);
    });
    // http://localhost:8080/index
    router. get('/:index',(req, res, next) => { sendFile ( pathJoin( req. params. index +'.html'), res, next);
    });
    module. exports = app. listen(PORT, err => {
        if (err){    
           return
         }
      console.log(`Listening at http://${HOST}:${PORT}\n`);
    })
    

    私服创建好了,安装下依赖。有坑。。。

    webpack和热加载版本太高太低都不行

    npm install webpack@3.10.0 --save-dev
    npm install webpack-dev-middleware --save-dev
    npm install webpack-hot-middleware@2.21.0 --save-dev
    npm install http-proxy-middleware --save-dev
    

    第四步:修改配置
    webpack.base.conf.js:

    'use strict'
    const utils = require('./utils')
    const webpack = require('webpack')
    const config = require('../config')
    const merge = require('webpack-merge')
    const path = require('path')
    const baseWebpackConfig = require('./webpack.base.conf')
    const CopyWebpackPlugin = require('copy-webpack-plugin')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
    const portfinder = require('portfinder')
    const HOST = process.env.HOST
    const PORT = process.env.PORT && Number(process.env.PORT)
    const devWebpackConfig = merge(baseWebpackConfig, {
        module:{
             rules: utils. styleLoaders({ sourceMap: config.dev. cssSourceMap. usePostCSS: true })  
    },
    // cheap-module-eval-source-map is faster for development
      devtool:
      config.dev.devtool,
        // these devServer options should be customized in /config/index.js
      devServer: {
         clientLogLevel:'warning', 
         historyApiFallback: {
             rewrites: [
                    { from: /.*/, to: path. posix.join(config. dev. assetsPublicPath, 'index.html')},
              ],
      },
      hot:true, 
      contentBase: false,
      // since we use CopyWebpackPlugin.
      compress: true,
      host: HOST || config. dev. host,
      port:PORT || config. dev. port, open:
     config. dev. autoOpenBrowser, overlay:
     config. dev.errorOverlay    
        ? { warnings: false,  errors: true}
         : false, 
      publicPath: config. dev. assetsPublicPath,
      proxy: config. dev.proxyTable,
      quiet: true, // necessary for FriendlyErrorsPlugin
      watchOptions: {
           poll: config. dev. poll,
      }
    },
    plugins: [ 
      new webpack. DefinePlugin({  'process.env': require('../config/dev.env')    
    }),
    new webpack. HotModuleReplacementPlugin(),
    new webpack. NamedModulesPlugin(),
    // HMR shows correct file names in console on update.
    new webpack. NoEmitOnErrorsPlugin(), 
    // https://github.com/ampedandwired/html-webpack-plugin
    //del   注释掉spa固定的单页出口  末尾动态配上出口
     
    // new HtmlWebpackPlugin({
        
    //   filename: 'index.html',
        
    //   template: 'index.html',
        
    //   inject: true
     
    // }),
      
    //end
    
    // copy custom static assets
    
    new CopyWebpackPlugin([
          {
              from: path. resolve(__dirname,  '../static'),
              to: config. dev. assetsSubDirectory,
              ignore:['.*']     
           }   
      ])
    ]
    
    //add
    
    .concat(utils. createHtmlWebpackPlugin ())
    //end
    })
    //del
    
    

    webpack.dev.conf.js:

    'use strict'
    const utils = require('./utils')
    const webpack = require('webpack')
    const config = require('../config')
    const merge = require('webpack-merge')
    const path = require('path')
    const baseWebpackConfig = require( './webpack.base.conf')
    const CopyWebpackPlugin = require('copy-webpack-plugin')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
    const portfinder = require( 'portfinder')
    const HOST = process env.HOST
    const PORT = process.env.PORT &&Number (process.env.PORT)
    const devWebpackConfig = merge(baseWebpackConfig, {
        module: {  rules: utils. styleLoaders({ sourceMap: config.dev.cssSourceMapusePostCSS: true})  
    },
    // cheap-module-eval-source-map is faster for development
      devtool: config.dev.devtool, 
    // these devServer options should be customized in /config/index.js
    //del  注掉SPA的服务器
    
    
    

    相关文章

      网友评论

          本文标题:手把手教你 vue-cli 单页到多页应用

          本文链接:https://www.haomeiwen.com/subject/jsrmsftx.html