美文网首页Vue.jsvueVue.js专区
模仿vue-cli搭建脚手架以及通用方法封装

模仿vue-cli搭建脚手架以及通用方法封装

作者: 何足道01 | 来源:发表于2019-05-14 22:21 被阅读0次
模仿搭建vue-cli

其实vue-cli本身就挺好用的,但是毕竟自己亲手搭的还是比较灵活,出了问题也方便解决,所以我们自己动手搭建一个脚手架

主要用到的工具

  • webpack
  • webpack-cli
  • axios
  • vue-router

package.json配置如下

{
  "name": "book",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --history-api-fallback",
    "build": "cross-env NODE_ENV=production webpack",
    "build-app": "cross-env NODE_ENV=app webpack",
    "start": "npm run dev"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-2": "^6.24.1",
    "clean-webpack-plugin": "^2.0.1",
    "config": "^3.0.1",
    "copy-webpack-plugin": "^5.0.3",
    "cross-env": "^5.2.0",
    "css-loader": "^2.1.0",
    "element-theme-chalk": "^2.5.4",
    "file-loader": "^3.0.1",
    "gulp": "^4.0.0",
    "gulp-clean-css": "^4.0.0",
    "gulp-css-wrap": "^0.1.2",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.9.0",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.6.0",
    "moment": "^2.24.0",
    "node-sass": "^4.11.0",
    "os": "^0.1.1",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2",
    "vue": "^2.6.6",
    "vue-loader": "^15.6.2",
    "vue-router": "^3.0.2",
    "vue-template-compiler": "^2.6.6",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.1.14",
    "webpack-theme-color-replacer": "^1.0.17"
  },
  "dependencies": {
    "axios": "^0.18.0",
    "element-ui": "^2.5.4",
    "mint-ui": "^2.2.13",
    "nprogress": "^0.2.0",
    "qs": "^6.6.0",
    "vue-ls": "^3.2.1",
    "vuex": "^3.1.0"
  }
}

webpack.config.js内容如下

const webpack = require('webpack')
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const dev_mode = process.env.NODE_ENV != 'production'
const config = require('config')
const os = require('os')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin")
//获取ip
//注意匹配127.0.0.1的情况
let host = ''
let obj = os.networkInterfaces()
for (n in obj) {
  obj[n].map(v => {
    ;
    /^[0-9]{1,}\.[0-9]{2,}\.[0-9]{1,}\.[0-9]{1,}$/.test(v.address) &&
      (host = v.address)
  })
}
module.exports = {
  entry: path.join(__dirname, 'src/main.js'),
  output: {
    filename: 'js/[name].[hash].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: process.env.NODE_ENV == 'app' ? "./" : "/"
  },
  resolve: {
    alias: {
      '@': path.resolve('src')
    },
    extensions: ['.js', '.vue', '.css', '.scss']
  },
  mode: (process.env.NODE_ENV == 'app' ? 'production' : process.env.NODE_ENV),
  module: {
    rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'stage-2']
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: dev_mode ? ['style-loader', 'css-loader'] : [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.less$/,
        use: dev_mode ? ['style-loader', 'css-loader', 'less-loader'] : ['css-loader', 'less-loader']
      },
      {
        test: /\.scss$/,
        use: dev_mode ? ['style-loader', 'css-loader', 'sass-loader'] : [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      },
      {
        test: /\.(gif|jpg|jpeg|woff(2)?|ttf|eot|svg)/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 1024,
            name: 'style/[name].[hash:6].[ext]'
          }
        }]
      }
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'src'),
    watchContentBase: true,
    hot: true,
    port: config.port,
    host: '0.0.0.0',
    proxy: {
      '/index.php': {
        target: 'http://api.guying.club',
        changeOrigin: true,
        secure: false
      }
    }
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './index.html',
      // favicon: path.resolve(__dirname, 'public/favicon.ico'),
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
      }
    }),
    new MiniCssExtractPlugin({
      filename: "style/[name].[hash].css",
      chunkFilename: 'style/[id].[hash:8].css',
    }),
    new CleanWebpackPlugin(),
    //拷贝theme.less用于换肤
    new CopyPlugin([{
      from: 'public',
      to: ''
    }])
  ],
  performance: {
    hints: false
  },
  optimization: {
    // 公共代码抽取
    // CommonsChunkPlugin 已弃用,使用optimization.splitChunks代替
    // 提取被重复引入的文件,单独生成一个或多个文件,这样避免在多入口重复打包文件
    splitChunks: {
      cacheGroups: {
        commons: {
          // 选择全部chunk
          chunks: "all",
          // 生成的公共代码文件名,惯用vendor
          name: "vendor",
          // 作用于
          test: /[\\/]node_modules[\\/]/
        }
      }
    }
  }
}

if (dev_mode) {
  module.exports.plugins = module.exports.plugins.concat([
    new webpack.HotModuleReplacementPlugin(),
    new webpack.DefinePlugin({
      LOCAL_ADDRESS: JSON.stringify(`http://${host}:${config.port}`)
    })
  ])
}

MiniCssExtractPlugin 插件来抽离vue文件中的style部分为单独的css文件,放到/css下

CleanWebpackPlugin 插件在打包前删除dist文件夹

CopyPlugin 插件复制index.less到dist文件夹,因为编译以后需要用less.modifyVars实现换肤,所以需要拷贝一份样式文件

然后是自己封装的axios请求

/**axios封装
 * 请求拦截、相应拦截、错误统一处理
 */
import axios from 'axios';
import QS from 'qs';

// 环境的切换
if (process.env.NODE_ENV == 'development') {    
    axios.defaults.baseURL = LOCAL_ADDRESS;
} else if (process.env.NODE_ENV == 'debug') {    
    axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {    
    axios.defaults.baseURL = '';
}



// 请求超时时间
axios.defaults.timeout = 10000;

// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

// 请求拦截器
axios.interceptors.request.use(    
    config => {
        // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
        return config;
    },    
    error => {        
        return Promise.error(error);    
    })

// 响应拦截器
axios.interceptors.response.use(    
    response => {     
        if (response.status === 200) {    
            return Promise.resolve(response);        
        } else {            
            return Promise.reject(response);        
        }    
    },
    //服务器状态码不是200的情况    
    error => {        
        if (error.response && error.response.status) {            
            switch (error.response.status) {                
                // 401: 未登录                
                // 未登录则跳转登录页面,并携带当前页面的路径                
                // 在登录成功后返回当前页面,这一步需要在登录页操作。                
                case 401:                    
                    router.replace({                        
                        path: '/login',                        
                        query: { redirect: router.currentRoute.fullPath } 
                    });
                    break;
                // 403 token过期                
                // 登录过期对用户进行提示                
                // 清除本地token和清空vuex中token对象                
                // 跳转登录页面                
                case 403:                     
                    Toast({                        
                        message: '登录过期,请重新登录',                        
                        duration: 1000,                        
                        forbidClick: true                    
                    });                    
                    // 清除token                    
                    localStorage.removeItem('token');                    
                    store.commit('loginSuccess', null);                    
                    // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
                    setTimeout(() => {                        
                        router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);                    
                    break; 
                // 404请求不存在                
                case 404:                    
                    Toast({                        
                        message: '网络请求不存在',                        
                        duration: 1500,                        
                        forbidClick: true                    
                    });                    
                break;                
                // 其他错误,直接抛出错误提示                
                default:                    
                    Toast({                        
                        message: error.response.data.message,                        
                        duration: 1500,                        
                        forbidClick: true                    
                    });            
            }            
            return Promise.reject(error.response);        
        }       
    }
);
/** 
 * get方法,对应get请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
export function get(url, params){    
    return new Promise((resolve, reject) =>{        
        axios.get(url, {            
            params: params        
        })        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {            
            reject(err.data)        
        })    
    });
}
/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
export function post(url, params) {    
    return new Promise((resolve, reject) => {         
        axios.post(url, QS.stringify(params))        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {     
            reject(err.data)        
        })    
    });
}
使用了vue-router后页面地址栏路径改变后刷新页面报错

这个主要时由于设置了vue-router为history模式,找不到路由下面的index.html页面,解决方式为配置nginx服务器的try_files,
本地解决方式为配置webpack-dev-server,增加 -history-api-fallback

使用vue-router以后地址栏改变找不到静态资源

配置webpack>output>publicPath:'/'

待续

相关文章

网友评论

    本文标题:模仿vue-cli搭建脚手架以及通用方法封装

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