mcya / JavaScriptExperience

👮 JavaScript Experience for web(JavaScript归类:简单入门+数组+经典循环+字符串+函数+Date日期+简单对象+BOM+DOM) +[ Issues(js实用技巧)]
28 stars 8 forks source link

从 0 搭建 V+B 基础配置解读篇 #37

Open mcya opened 6 years ago

mcya commented 6 years ago

MacBook

mcya commented 6 years ago

初始化

# 输入 vue 检测是否安装过vue-cli, 如安装直接进入 vue init webpack

$ npm install --global vue-cli
# 输入 vue 检测是否安装成功

$ vue init webpack
# 一直按回车, 中间的停顿输入 即是 title 的值

$ npm run dev

vue npm/cnpm install或者npm run dev 报错解决思路之一

Mac npm install -g vue-cli 报错解决

vue npm run dev 启动 报错 dev: node build/dev-server.js 解决

构建项目参考官方文档

http://vuejs-templates.github.io/webpack/ http://vuejs-templates.github.io/webpack/

mcya commented 6 years ago

项目启动成功后 自动打开浏览器

config/index.js

autoOpenBrowser: true, //是否自动打开浏览器,默认为 false

mcya commented 6 years ago

build 选项的配置

用vue-cli 自动构建的目录里面(环境变量及其基本变量的配置)

build: {
    // 初始项目会在执行 `npm run build` 之后自动生成。
    index: path.resolve(__dirname, 'dist/index.html'), // index.html会被生成
    assetsRoot: path.resolve(__dirname,  'dist'), //编译后的文件都会在这里
    assetsSubDirectory: 'static', //这里是写对应的静态文件或者插件,如 jq.min.js /common.js,使用的文件需要在main.js中引入。
    assetsPublicPath: '/', //如果你的后台框架对静态资源url前缀要求,你仅需要改变这个参数。在内部,这个是被webpack当做output.publicPath来处理的
    productionSourceMap: true, // 在构建生产环境版本时是否开启source map。微客项目 false
  },
dev: {
    port: 8080,
    proxyTable: {} //定义开发服务器的代理规则。
  }

正常开发中,build的配置默认取原值,productionSourceMap可选择性改为 “false”

dev的配置正常都需要更改,端口号一般取非 8080 之外的端口。proxyTable需要正确配置路径。

mcya commented 6 years ago

proxyTable配置

类型一:假设我现在需要将我本地的http://localhost:8080/thispath代理到目标地址http://11.38.58.28:3838/newpath 下,那么我就可以这样做:

module.exports= {
  dev: {
    proxyTable: {
      '/somepath': {
        target: 'http://a.b.com',
        changeOrigin: true,
        pathRewrite: {
          '^/thispath': '/newpath'
        }
      }
    }
  }
}

类型二:全局替代(一般的项目直接使用这种方式)

   proxyTable: {
     '/api': {
       target: 'http://11.38.58.28:3838/newpath', //服务器路径
       changeOrigin: true,
       pathRewrite: {
         '^/api': ''
        //在调用的时候,直接定义URL为该服务器路径下即可:var url = '/paths/postfun.do'
        // 即:api相当于一个别名,代指 'http://11.38.58.28:3838/newpath'
       }
     }
    },

使用 https

proxyTable: {
     '/api': {
       target: 'https://11.38.58.28:3838/newpath', //服务器路径
       changeOrigin: true,
       pathRewrite: {
         '^/api': ''
        //在调用的时候,直接定义URL为该服务器路径下即可:var url = '/paths/postfun.do'
        // 即:api相当于一个别名,代指 'http://11.38.58.28:3838/newpath'
       },
       // https适配
       secure: false,
       headers: {
            Referer: 'https://11.38.58.28:3838/newpath'
       }
     }
    },
mcya commented 6 years ago

this.$http.post 的声明方式

mcya commented 6 years ago

配置config/index.js (常规的 production环境)

(一)文档理解

var path = require('path')
  module.exports = {
    build: { // production 环境

      // 使用 config/prod.env.js 中定义的编译环境,即用来指定当前环境。
      env: require('./prod.env'), 

      index: path.resolve(__dirname, '../dist/index.html'),  // 编译输入的 index.html 文件
      assetsRoot: path.resolve(__dirname, '../dist'),  // 编译输出的静态资源路径

      assetsSubDirectory: 'static', // 编译输出的二级目录
      assetsPublicPath: '/',  // 编译发布的根目录,可配置为资源服务器域名或 CDN 域名

      // 是否开启 cssSourceMap,sourcmap是用来debug编译后文件的,通过映射到编译前文件来实现
      productionSourceMap: true, 

      // 是否开启 gzip (如需对接口进行加密,需要开启gzip ) 如果要压缩必须安装compression-webpack-plugin
      productionGzip: false, 

      productionGzipExtensions:  ['js', 'css']  // 需要使用 gzip 压缩的文件扩展名

      // 下面是用来开启编译完成后的报告,可以通过设置值为true和false来开启或关闭
      // 下面的process.env.npm_config_report表示定义的一个npm_config_report环境变量,可以自行设置。初始化安装默认为这样。
       bundleAnalyzerReport: process.env.npm_config_report
    },
    dev: { // dev 环境

     // 使用 config/dev.env.js 中定义的编译环境
      env: require('./dev.env'), 

      // 运行测试页面的端口,一般不取8080,该端口很可能已经被占用,直接用8080可能无法启动。
      port: 8080, 

      assetsSubDirectory: 'static', // 编译输出的二级目录

      assetsPublicPath: '/', // 编译发布的根目录,可配置为资源服务器域名或 CDN 域名

      // 需要 proxyTable 代理的接口(可跨域) vue-cli使用这个功能是借助 http-proxy-middleware 插件,一般解决跨域请求api
      proxyTable: {}, 

      // 是否开启 cssSourceMap,即是提示css报错,其实直接看控制台即可,直接改为 false。
      cssSourceMap: false 
    }
  }

(二)举个来自真实项目的例子

// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')

module.exports = {
  build: {
    env: require('./prod.env'),
    index: path.resolve(__dirname, '../dist/index.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: './',
    productionSourceMap: false,
    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  },
  dev: {
    env: require('./dev.env'),
    port: 8686,
    autoOpenBrowser: true,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    // proxyTable: {},
    proxyTable: {
     '/api': {
      target: 'http://1.2.2.8:3822/newpath',
       changeOrigin: true,
       pathRewrite: {
         '^/api': ''
       }
     }
    },
    // CSS Sourcemaps off by default because relative paths are "buggy"
    // with this option, according to the CSS-Loader README
    // (https://github.com/webpack/css-loader#sourcemaps)
    // In our experience, they generally work as expected,
    // just be aware of this issue when enabling this option.
    cssSourceMap: false
  }
}
mcya commented 6 years ago

build/webpack.base.conf.js 配置

(一)解读

//引入依赖模块
var path = require('path')
var config = require('../config') // 获取配置
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')

var env = process.env.NODE_ENV
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
// various preprocessor loaders added to vue-loader at the end of this file
var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)/* 是否在 dev 环境下开启 cssSourceMap ,在 config/index.js 中可配置 */
var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)/* 是否在 production 环境下开启 cssSourceMap ,在 config/index.js 中可配置 */
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd /* 最终是否使用 cssSourceMap */

module.exports = {
  entry: {   // 配置webpack编译入口
    app: './src/main.js' 
  },
  output: {    // 配置webpack输出路径和命名规则
    path: config.build.assetsRoot, // webpack输出的目标文件夹路径(例如:/dist)
    publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,  // webpack编译输出的发布路径(判断是正式环境或者开发环境等)
    filename: '[name].js'   // webpack输出bundle文件命名格式,基于文件的md5生成Hash名称的script来防止缓存
  },
  resolve: {
    extensions: ['', '.js', '.vue', '.scss'],  //自动解析确定的拓展名,使导入模块时不带拓展名
    fallback: [path.join(__dirname, '../node_modules')],
    alias: {  // 创建import或require的别名,一些常用的,路径长的都可以用别名
      'vue$': 'vue/dist/vue',
      'src': path.resolve(__dirname, '../src'),
      'assets': path.resolve(__dirname, '../src/assets'),
      'components': path.resolve(__dirname, '../src/components'),
      'scss_vars': path.resolve(__dirname, '../src/styles/vars.scss')
    }
  },
  resolveLoader: {
    fallback: [path.join(__dirname, '../node_modules')]
  },
  module: {
    loaders: [
        {
            test: /\.vue$/, // vue文件后缀
            loader: 'vue'   //使用vue-loader处理
        },
        {
            test: /\.js$/,
            loader: 'babel',
            include: projectRoot,
            exclude: /node_modules/
        },
        {
            test: /\.json$/,
            loader: 'json'
        },
        {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            loader: 'url',
            query: {
              limit: 10000,
              name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
        },
        {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            loader: 'url',
            query: {
              limit: 10000,
              name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
            }
        }
    ]
  },
  vue: {    // .vue 文件配置 loader 及工具 (autoprefixer)
    loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }), //// 调用cssLoaders方法返回各类型的样式对象(css: loader)
    postcss: [
      require('autoprefixer')({
        browsers: ['last 2 versions']
      })
    ]
  }
}
mcya commented 6 years ago

build/webpack.base.conf.js 配置

(二)举个来自真实项目的例子(APP)

var path = require('path')
var utils = require('./utils')

var projectRoot = path.resolve(__dirname, '../')
const vuxLoader = require('vux-loader') //使用 vux-loader 对应 vux-ui

var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

let webpackConfig = { //定义变量,使用UI框架,这里使用的是 vux-ui
  watch: true, //开启watch, 需要配置 resolve('node_modules/vue-bulma-tooltip')
  entry: {
  //  app: './src/main.js'
  app: ["babel-polyfill", "./src/main.js"] //需引入babel-polyfill,主要是为了es6能在较低版本浏览器中运行
  },
  externals: { //AMap
     //修改webpack配置,加入exrernals字段; 在mouted的时候解析地址数据,并根据传回的地区编号跟后台进行对接(使用的字段为area_id);
    'AMap': 'AMap'
  },
  output: { //输出
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        // watch entry 对应 resolve('node_modules/vue-bulma-tooltip')
        include: [resolve('src'), resolve('test'), resolve('node_modules/vue-bulma-tooltip')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

// module.exports  在这里进行配置
module.exports = vuxLoader.merge(webpackConfig, {
  plugins: ['vux-ui', 'progress-bar', 'duplicate-style']
})
mcya commented 6 years ago

webpack.prod.conf.js 配置, 该文件可直接取初始化,不做更改

(一) 解读

'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')

// 一个可以合并数组和对象的插件
const merge = require('webpack-merge')

const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const env = process.env.NODE_ENV === 'testing'
  ? require('../config/test.env')
  : require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: { //输出配置
    path: config.build.assetsRoot, //目录
    filename: utils.assetsPath('js/[name].[chunkhash].js'), //文件名格式
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') //没有指定输出名的文件输出的文件名格式
  },
  plugins: [ //配置插件项
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new UglifyJsPlugin({ // 丑化压缩代码
      uglifyOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    // extract css into its own file
    new ExtractTextPlugin({ // 抽离css文件
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      allChunks: true,
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      // 生成html文件的名字,路径和生产环境下的不同,要与修改后的publickPath相结合,否则开启服务器后页面空白
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        : config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vendor modules does not change
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks (module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig
mcya commented 6 years ago

build/webpack.base.conf.js 配置

(二)举个来自真实项目的例子(APP)

var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

var env = config.build.env

var webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      sourceMap: true
    }),
    // extract css into its own file
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css')
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true
      }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module, count) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      chunks: ['vendor']
    }),
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  var CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig
mcya commented 6 years ago

build.js配置 亦可直接取初始化文件

// https://github.com/shelljs/shelljs
require('./check-versions')() // 检查 Node 和 npm 版本
require('shelljs/global')  // 使用了 shelljs 插件,可以让我们在 node 环境的 js 中使用 shell
env.NODE_ENV = 'production'

var path = require('path') 
var config = require('../config') // 加载 config.js
var ora = require('ora') // 一个很好看的 loading 插件
var webpack = require('webpack')  // 加载 webpack
var webpackConfig = require('./webpack.prod.conf')  // 加载 webpack.prod.conf

console.log( //  输出提示信息 ~ 提示用户请在 http 服务下查看本页面,否则为空白页
  '  Tip:\n' +
  '  Built files are meant to be served over an HTTP server.\n' +
  '  Opening index.html over file:// won\'t work.\n'
)

var spinner = ora('building for production...')  // 使用 ora 打印出 loading + log
spinner.start()  // 开始 loading 动画

/* 拼接编译输出文件路径 */
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
rm('-rf', assetsPath) /* 删除这个文件夹 (递归删除) */
mkdir('-p', assetsPath) /* 创建此文件夹 */ 
cp('-R', 'static/*', assetsPath) /* 复制 static 文件夹到我们的编译输出目录 */

webpack(webpackConfig, function (err, stats) {  //  开始 webpack 的编译
    // 编译成功的回调函数
  spinner.stop()
  if (err) throw err
  process.stdout.write(stats.toString({
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }) + '\n')
})