通过Webpack进行全局Vue组件曝光

时间:2018-03-09 12:03:50

标签: webpack vue.js vue-component

基本上,我正在尝试为MVC项目制作混合结构。前端框架将由Webpack和VueJS管理。然而,经过数周的修补和与Vue一起获得适当的Webpack知识后,我一直无法实现我想要的目标。

This is the structure of how webpack works in the MVC project

所以上面是项目结构,但特别是webpack层。 webpack文件夹首先由Webpack打包到wwwroot / dist文件夹中,最终会像这样;

wwwroot structure or items

从这里开始,我们将能够将包导入到MVC视图的主要布局中,然后我们可以将Vue内联应用于每个视图。这样做的目标是我们可以首先,

  1. 使用Webpack捆绑样式和常用的js库
  2. 能够利用Vue和Vue的组件,同时能够创建块结构(0.js,1,2 ......)
  3. 由于2,我们将能够向CSR(客户端渲染)倾斜。
  4. 这是我的webpack.config.js供参考。

    const path = require('path');
    const webpack = require('webpack');
    const MergeIntoSingleFilePlugin = require('webpack-merge-and-include-globally');
    const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    const extractCSS = new ExtractTextPlugin('bundle.css');
    
    // Declaring multiple modules
    // https://stackoverflow.com/questions/16631064/declare-multiple-module-exports-in-node-js
    module.exports = function (env) {
        env = env || {};
        var isProd = env.NODE_ENV === 'production';
    
        // Setup base config for all environments
        var config = {
            entry: {
                main: './Webpack/js/main'
            },
            output: {
                // The format for the outputted files
                filename: '[name].js',
                // Put the files in "wwwroot/js/"
                path: path.resolve(__dirname, 'wwwroot/dist/')
            },
            devtool: 'eval-source-map',
            resolve: {
                alias: {
                    'vue': 'vue/dist/vue.esm.js' // Use the full build
                },
                extensions: ['.js', '.jsx']
            },
            plugins: [
                extractCSS,
                new webpack.ProvidePlugin({ 
                    $: "jquery",
                    jQuery: "jquery",
                    "window.jQuery": "jquery'",
                    "window.$": "jquery",
                    "dt": "datatables.net",
                    Popper: ['popper.js', 'default']
                }),
                new MergeIntoSingleFilePlugin({
                    files: {
                        // Table related libraries
                        "tbl.js": [
                            'node_modules/datatables.net/js/jquery.dataTables.js',
                            'node_modules/datatables.net-bs4/js/dataTables.bootstrap4.js'
                        ],
                        "tbl.css": [
                            'node_modules/datatables.net-bs4/css/dataTables.bootstrap4.css',
                            'node_modules/datatables.net-buttons-bs4/css/buttons.bootstrap4.min.css'
                        ],
                        "duo-web.js": [
                            'Webpack/js/securo/Duo-Web-v2.js'
                        ]
                    }
                })
            ],
            module: {
              rules: [
                { test: /\.css$/, use: extractCSS.extract(['css-loader?minimize']) },
                { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' },
                { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' },
                // Recognise VueJS
                {
                    test: /\.vue$/,
                    loader: 'vue-loader'
                },
                // Expose jQuery globally
                // https://stackoverflow.com/questions/47469228/jquery-is-not-defined-using-webpack
                {
                    test: require.resolve('jquery'),
                    use: [{
                        loader: 'expose-loader',
                        options: 'jQuery'
                    },{
                        loader: 'expose-loader',
                        options: '$'
                    }]
                },
                {
                    test: require.resolve('bootbox'),
                    use: [{
                        loader: 'expose-loader',
                        options: 'bootbox'
                    }]
                },
                {
                    test: require.resolve('clipboard'),
                    use: [{
                        loader: 'expose-loader',
                        options: 'Clipboard'
                    }]
                },
              ],
              loaders: [
                {
                    test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                    loader: "url-loader"
                },
                {
                    test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                    loader: "url-loader"
                },
                {
                    test: /\.vue$/,
                    loader: 'vue-loader'
                }
              ]
            }
          };
    
          // Alter config for prod environment
          if (isProd) {
            config.devtool = 'source-map';
            config.plugins = config.plugins.concat([
                new UglifyJsPlugin({
                    sourceMap: true
                }),
                // https://vuejs.org/v2/guide/deployment.html
                new webpack.DefinePlugin({
                    'process.env': {
                        NODE_ENV: '"production"'
                    }
                })
            ]);
          }
    
          return config;
    };
    
    function toObject(paths) {
        var ret = {};
        paths.forEach(function (path) {
            ret[path.split('/').slice(-1)[0]] = path;
        });
        return ret;
    }
    

    这是main.js

    // Load the css first
    import 'bootstrap/dist/css/bootstrap.css';
    import '../css/counter-ui.css';
    import '../css/site.css';
    import 'font-awesome/css/font-awesome.css';
    // Then the js
    import Vue from 'vue'; // Compile included builds https://github.com/nuxt/nuxt.js/issues/1142
    import 'jquery';
    import 'jquery-validation';
    import 'jquery-validation-unobtrusive';
    import 'popper.js';
    import 'bootstrap';
    import 'bootstrap-datepicker';
    import 'bootbox';
    import 'clipboard';
    import 'magicsuggest';
    import 'nouislider';
    import '../js/counter-ui.js';
    import 'formcache';
    
    // Vue Components
    Vue.component(/* webpackChunkName: "base-select" */ 'base-select', () => import('./components/base-select.vue'));
    
    // Expose Vue globally
    // https://stackoverflow.com/questions/45388795/uncaught-referenceerror-vue-is-not-defined-when-put-vue-setting-in-index-html
    window.Vue = Vue;
    
    $(function() {
        $('.datepicker').datepicker();
        $('.dropdown-toggle').dropdown();
        $('[data-toggle="popover"]').popover({
            animation: true,
        });
    });
    
    // Array Dupes Filter
    // https://stackoverflow.com/questions/6940103/how-do-i-make-an-array-with-unique-elements-i-e-remove-duplicates
    function OmitArrDupes(a) {
        var temp = {};
        for (var i = 0; i < a.length; i++)
            temp[a[i]] = true;
        var r = [];
        for (var k in temp)
            r.push(k);
        return r;
    }
    

    正如您所看到的,webpack仅用于捆绑常见的库和样式,同时,它用于存储Vue和我将要制作的组件,并帮助解决所有肮脏的工作。

    然后你最终会得到:

    enter image description here

    这就是你如何能像我一样陷入困境,或者据我所知,愚蠢。在将其用于生产用途后,我正在变成一个degen。

    根据chrome上的控制台错误判断,由于组件方面的问题,加载了0.js但是已中止。到底是怎么回事?我真的很想知道这是如何实际工作的。从来没有如此深入到前端。

    编辑这可能是由于组件的语法。可能是错的?嗯..但如果是这样的话,Webpack将不在话题中。

    <template>
        <div :class="{ __disabled: disabled }" class="dropdown">
            <button @click="toggle" class="btn btn-primary dropdown-toggle">
                {{ currOption.name }}
            </button>
            <div v-if="opened" class="dropdown-menu">
                <div v-for="o in options" :value="getVal(value)" @click="change(o)" :class="{__active: getVal(o) == getVal(value)}" class="dropdown_item">{{ getLabel(o) }}</div>
            </div>
        </div>
    </template>
    
    <script>
        export default {
            name: "base-select",
            data() {
                return {
                    opened: false,
                    currOption: {}
                }
            },
            methods: {
                getVal(opt) {
                    return !this.valueKey ? opt : opt[this.valueKey];
                },
                getLabel(opt) {
                    return !this.labelKey ? opt : opt[this.labelKey];
                },
                change(opt) {
                    this.$emit('input', opt)
                    this.opened = false;
    
                    if (this.onChange !== undefined) {
                        this.onChange(this.value);
                    }
                },
                toggle() {
                    if (this.disabled) {
                        return
                    }
    
                    // flip
                    this.opened = !this.opened;
                }
            },
            props: {
                value: {
                    required: true
                },
                options: {
                    type: Array,
                    required: true
                },
                valueKey: {
                    type: String,
                    required: false
                },
                labelKey: {
                    type: String,
                    required: false
                },
                onChange: {
                    type: Function,
                    required: false
                },
                disabled: {
                    type: Boolean,
                    default: false
                }
            }
        }
    </script>
    
    <style scoped>
    
    </style>
    

    更新2 尝试创建一个test.vue组件,其中包含一个带有hi的模板

    也没用。同样的错误。

2 个答案:

答案 0 :(得分:1)

我认为这可能是由最近为vue-loader更改的异步/动态导入语法引起的,现在使用“... ES模块在内部利用webpack 3范围提升”。 (请参阅下面的发行说明

尝试像这样导入

Vue.component('base-select',  () => import('./components/base-select.vue').then(m => m.default));

https://github.com/vuejs/vue-loader/releases/tag/v13.0.0

编辑尝试将输出块文件名更改为相对路径,如下所示。

  output: {
         // The format for the outputted files
        filename: '[name].js',

        // Put the files in "wwwroot/js/"
        path: path.resolve(__dirname, 'wwwroot/dist/')

        // Set chuck file name
         chunkFilename:'../../js/[name].bundle.js'
    },

答案 1 :(得分:1)

除了@skribe所说的,

Vue.component('base-select',  () => import('./components/base-select.vue').then(m => m.default));

这应该在全球范围内公开所有组件。为了支持.NET Core调用的路由,您必须添加一个名为 publicPath 的附加参数。 publicPath允许您相对于已声明的根公共路径全局公开文件,在本例中为publicPath。

module.exports = function (env) {
    env = env || {};
    var isProd = env.NODE_ENV === 'production';

    // Setup base config for all environments
    var config = {
        entry: {
            main: './Webpack/js/main'
        },
        output: {
            // The format for the outputted files
            filename: '[name].js',
            // Put the files in "wwwroot/js/"
            path: path.resolve(__dirname, 'wwwroot/dist/'),
            // ============ ADD THE LINE BELOW ============= //
            publicPath: '/dist/'
        },