天天看点

Webpack导入Vue实战

上一文中做了一个基本的Webpack环境搭建,对于webpack+vue组合的搭建过程,网上搜索资料不少,好好研究任意一篇就可以掌握原理和过程,本文没有利用vue-cli脚手架工具搭建,而是自己动手基于webpack环境导入vue项目。

实战如下:(实际执行过程中碰见不少坑,都研究后解决了,大概的步骤记录下来,供参考)

1:基本的webpack环境搭建:引入模块:css-loader,style-loader,webpack, webpack-dev-server等,对照Webpack环境搭建;

2:npm i -D vue-loader;//解析转化.vue文件,

     npm i -D vue-template-complier //将vue-loader提取出的html模板编译成js代码;

3:npm i -S vue;

-D 保存在pakage.json文件的devDependencies节点;-S保存在dependencies节点下;

由于vue项目采用了ES6语法, 需要接入Babel 转化浏览器可以识别的ES5语法

4: Babel需要引入下面的模块

babel-plugin-transform-runtime //Babel转化插件

babel-runtime//上面的插件依赖runtime

babel-core babel-loader //当前webpack引入babel必须依赖的模块

babel-preset-env //presets 预设:告诉babel使用了何种ES6语法特性;env 为包含当前最新特性;

实际配置的package.json如下;

"devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^8.0.6",
    "babel-plugin-runtime": "^1.0.7",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.7.0",
    "css-loader": "^2.1.1",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "html-webpack-plugin": "^3.2.0",    
    "style-loader": "^0.23.1",
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^4.32.2",
    "webpack-cli": "^3.3.2",
    "webpack-dev-server": "^3.4.1"
  },
  "dependencies": {
    "vue": "^2.6.10"
  }
           

采坑经验: 上面的安装不要一会是npm 一会是cnpm,会导致版本不兼容,编译失败

5:npm run dev 运行:

Webpack导入Vue实战

vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config. 

加入 VueLoaderPlugin即可,如下所示:webpack.config.js配置为: 

const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports= {
      ... //省略其他配置
    plugins: [
        new VueLoaderPlugin()
    ]
}
           

 6: 到这里,开发一个.vue单组件页面,然后通过import xx from '/xx.vue',最简单vue项目就可进行了,但实际项目中远非如此,涉及到优化,打包等诸多环节,下面再来一个一个问题解决;

Webpack导入Vue实战

7:区分开发和生产环境

Webpack导入Vue实战

webpack.base.config.js内容为:

const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const ExtractTextPlugin = require ('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require ('html-webpack-plugin');

module.exports= {
    entry: path.resolve(__dirname,"../src/main.js"),
    output: {
        filename:'[name]_[hash:8].js',
        chunkFilename: '[id].[chunkhash].js',
        path:path.resolve(__dirname,"../dist"),
    },
    module: {
        rules: [{
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.css$/,
                loader:ExtractTextPlugin.extract({
                    fallback:'style-loader',
                    use:'css-loader',
                    publicPath:"../dist"
                })
            },
            {
                test: /\.js$/,
                use: 'babel-loader',
                exclude: /node_modules/
              },
              
        ]
    },
    resolve: {
        extensions: ['.vue', '.js', '.json']
    },
   
    plugins: [
        new VueLoaderPlugin(),
        new ExtractTextPlugin({
            filename:'assest/[name]_[hash:8].css', //打包后的文件路径以及文件名       
        }),
        new HtmlWebpackPlugin ({
            template: path.resolve (__dirname, '../index.html')
         }),
    ]
}
           

 webpack.dev.config.js内容为:

需要先npm i -D webpack-merge

const merge = require('webpack-merge');
const base = require('./webpack.base.config');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');

var config = {
    devtool: 'source-map',
    devServer: {
        quiet: true,
        overlay: true,
      },
    plugins: [
         new FriendlyErrorsWebpackPlugin({
            compilationSuccessInfo: {
              messages: ['You application is running here http://localhost:8080'],           
            },           
            clearConsole: true,
          })
    ]
}
module.exports= merge(base,config) 
           

 webpack.prod.config.js内容为:

const merge = require('webpack-merge');
const base = require('./webpack.base.config');
const DefinePlugin = require ('webpack/lib/DefinePlugin');

module.exports= merge(base,{
    module: {
        rules: [{
                test: /\.js$/,
                loader: 'babel-loader',
                query:{ // 解决error: Unexpected token: punc (() 
                    presets:['es2015']
                },
                exclude: /node_modules/
            },
        ]
    },   
    mode: 'production',
    plugins: [
        new DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production')
        }),
    ]
})
           

 package.json配置中script节点如下:

"scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.config.js",
    "build": "webpack --config build/webpack.prod.config.js"
  },
           

开发测试:npm run dev 

打包发布:npm run build 可以看到输出为:

Webpack导入Vue实战

7:JS、CSS 压缩处理

在第6步中设置了 mode='production' webpack自动对js压缩了,但css未被压缩处理,这里使用optimize-css-assets-webpack-plugin插件来压缩优化处理:

const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

 optimization: {
        minimizer: [new OptimizeCssAssetsPlugin({})],
    },
 plugins: [       
        new OptimizeCssAssetsPlugin({
            assetNameRegExp: /\.css$/g,
            cssProcessor: require('cssnano'),
            cssProcessorPluginOptions: {
              preset: ['default', { discardComments: { removeAll: true } }],
            },
            canPrint: true
          })
    ]
           
Webpack导入Vue实战

8:css 中引用图片的处理:

 此处不经意就会碰到404的错误:资源找不到,多半是引用路径导致的。本人在此也折腾了一会:

在实战的这个小例子中:保持开发css引用的图片路径和打包输出的路径目录结果保持一致,我把这个目录结果调整了下:

最终结果如下图所示:

Webpack导入Vue实战

使用file-loader+url-loader 模块加载图片,并调整下webpack.base.config.js的相关位置:

module: {
        rules: [
            {
                test: /\.(gif|jpg|jpeg|png|svg)$/,
                use: [{
                    loader: 'url-loader',
                        options: {
                            limit: 1024*30,// 当文件大小小于limit byte时会把图片转换为base64编码的dataurl,否则返回普通的图片
                            name: 'assest/images/[name]-[hash:5].[ext]', // 图片文件名称加上内容哈希
                            fallback: 'file-loader',
                            publicPath:"../../",
                        }
                    }]
            }
        ]
    },
           

注意上图中 publicPath的处理为相对路径。

写一个css测试如下:

<template>
    <div class="bg-main">       
        <dcstop></dcstop>
    </div>
</template>

<style scoped>
.bg-main
{
    position:absolute; 
    top:0;
    left:0; 
    width:100%; 
    height:100%; 
    background-image: url('./assest/images/monitorbg.png');
    background-repeat: no-repeat;
}
</style>
           
Webpack导入Vue实战

上面一个大的背景图片,下面是一个小图片的url,可以看到url-loader的处理base64编码后的字符串; 

Webpack导入Vue实战

经过一通上面的操作,解决了webpack vue的导入,开发和正式环境的区分,含基本的优化:js压缩,css压缩等,也解决了图片资源的路径问题,还差一个css的预处理。这里选择本人比较喜欢的stylus

9: CSS预处理的支持:这里选用stylus:

cnpm i -D stylus stylus-loader

{
                test: /\.styl(us)?$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'stylus-loader'
                ]
              },
           

注意上面的正则表达式,如果写成 test: /\.styl$/  就报错:

Webpack导入Vue实战

Module parse failed: Unexpected character 

You may need an appropriate loader to handle this file type

Webpack导入Vue实战

 所以碰见问题,一定不要被问题吓倒了,先尝试根据错误去分析下问题的大概原因,并去相关官网搜索关键字或百度关键字,事物之间都有千丝万缕的联系,从别人的说法中,可以一层一层去找问题问题,只要功夫到位,问题一定会解决。

本文完。 

附加一份完整的webpack.dev.config.js配置项:

const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const ExtractTextPlugin = require ('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require ('html-webpack-plugin');
const { CleanWebpackPlugin }  = require('clean-webpack-plugin');
module.exports= {
    entry: path.resolve(__dirname,"../src/main.js"),
    output: {
        filename:'[name]_[hash:8].js',
        chunkFilename: '[id].[chunkhash].js',
        path:path.resolve(__dirname,"../dist"),
        publicPath: '/'
    },
    module: {
        rules: [{
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.css$/,
                loader:ExtractTextPlugin.extract({
                    fallback:'style-loader',
                    use:'css-loader',
                    publicPath:"../dist"
                })
            },
            {
                test: /\.js$/,
                use: 'babel-loader',
                exclude: /node_modules/
              },
              {
                test: /\.styl(us)?$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'stylus-loader'
                ]
              },
            {
                test: /\.(gif|jpg|jpeg|png|svg)$/,
                use: [{
                    loader: 'url-loader',
                        options: {
                            limit: 1024*30,// 当文件大小小于limit byte时会把图片转换为base64编码的dataurl,否则返回普通的图片
                            name: 'assest/images/[name]-[hash:5].[ext]', // 图片文件名称加上内容哈希
                            fallback: 'file-loader',
                            publicPath:"../../",
                        }
                    }]
            }
        ]
    },
    resolve: {
        extensions: ['.js', '.json','.vue','.styl']
    },
   
    plugins: [
        new VueLoaderPlugin(),
        new ExtractTextPlugin({
            filename:'assest/style/[name]_[hash:8].css', //打包后的文件路径以及文件名       
        }),
        new HtmlWebpackPlugin ({
            template: path.resolve (__dirname, '../index.html')
         }),
         new CleanWebpackPlugin({
            root: path.resolve(__dirname, '../dist'),   //根目录
         }),
    ]
}
           

继续阅读