1

一、使用vue-cli脚手架创建一个最简单项目

我们通过vue的脚手架工具,执行vue create vue-demo 可以生成最初项目。从最初的脚手架初始化项目中,我们可以看到项目根目录下主要有一个public目录src目录,其中public目录下主要就是一个index.html文件,这个index.html文件的作用就是为当前vue项目提供一个html模板,因为Vue实例要想挂载,即要想显示到html模板上,必须给其提供一个挂载点,所以模板中必须存在一个<div id="app"></div>,如:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue-webpack</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
接下来,看一下src目录,该目录下主要有main.jsApp.vue两个文件,其中main.js就是整个Vue项目的入口文件App.vue就是整个Vue项目的首页,即当前单页面应用的首页。整个Vue项目是通过webpack打包来启动项目,但是我们发现,整个项目中并没有webpack的配置文件。因为其并没有对外暴露webpack的配置文件,如果我们需要修改webpack的配置文件,那么可以在项目根目录下新建一个vue.config.js文件, 然后在configureWebpack中进行配置文件的修改,内容如下:
// vue.config.js
module.exports = {
  configureWebpack: {
    mode: "development",
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
  }
}
当然,如果想要查看默认的webpack文件到底给我们配置了什么,我们可以通过在项目根目录下执行 vue inspect > output.txt来大体查看webpack配置文件内容

二、vue项目的启动过程

vue项目的启动过程就是,首先以src目录下的main.js为项目入口进行打包,然后将打包后的js文件,自动插入到public目录下的index.html模板中,然后加载index.html文件并执行打包后的js文件。首先看一下main.js中的具体内容,如:
// vue.config.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false // 禁用生产模式下的提示信息

new Vue({
  render: (h) => {
    return h(App)
  },
}).$mount('#app')
我们可以看到main.js中就是引入了一下vue.js并创建一个Vue实例对象,然后调用$mount()方法进行了一下mount
需要注意的是,传入$mount()方法的参数,必须和public目录下的index.html中的id值一致,否则Vue实例将找不到挂载点挂载。
那么Vue实例挂载到页面后显示什么内容呢?答案就是,创建Vue实例的时候配置的render()函数,当Vue实例创建完成后,通过调用$mount()函数开始Vue实例的挂载,挂载的过程中,会执行render()函数,render()函数执行会产生一个虚拟节点,然后将虚拟节点转换为真实DOM节点并挂载到页面中。需要注意的时候,Vue实例挂载后,public目录下的index.html文件中的<div id="app"></div>会被渲染结果替换掉,即覆盖掉。

其中有一行Vue.config.productionTip = false,该行代码的作用就是禁用生产模式下的提示警告,简单说就是,如果当前项目是在production模式下,那么就禁用一些无用的警告信息提示;如果当前项目是在development模式下,那么就不禁用,而是正常提示警告信息,通常就是禁用如下提示,如:
You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
再看一下App.vue,App.vue就是一个Vue的组件,其会被vue-loader处理,然后交给render()渲染函数处理并渲染
// App.vue
<template>
  <div id="app">
    {{msg}}
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "hello msg"
    }
  }
}
</script>
项目中通过import Vue from "vue"引入的其实是运行时的vue,因为我们可以通过查看vue的webpack配置文件,我们可以看到,其配置了一个alias别名,所以当引入vue的时候,找的是vue/dist/vue.runtime.esm.js,这是一个runtime运行时的版本,而运行时的vue版本,是不支持template配置的,因为运行时的版本没有编译器,所以无法将template模板内容编译为渲染函数
resolve: {
    vue$: 'vue/dist/vue.runtime.esm.js' // $表示精确匹配
}
如果使用的是运行时的版本,那么当配置了template的时候,项目运行就会输出如下警告信息,即你当前使用的是运行时的vue,此时编译器不可用,即不包含编译器,因为编译器的作用就是,将template模板编译为渲染函数,所以你可以自己手动配置一个render渲染函数或者使用带编译器的vue进行构建
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build

vue-type.png

如果想要使用带编译器的版本,我们可以引入 "vue.esm.js"。这里有点比较难于理解的地方就是,我们引入的App.vue可以直接交给渲染函数的createElement(App)函数进行处理,因为vue-loader对.vue文件进行了转换,我们可以以下面的方式理解,如:
import Vue from "vue";
// App.vue start
const _vm = new Vue({
    data() {
        return {
            msg: "hello msg."
        }
    }
});
// import App from "./App.vue"; 引入后等价于如下
const App = ["div", {attrs: {"id":"app"}},_vm.msg];
// App.vue end
let vm = new Vue({
    render: (h) => {
        return h(...App);
    }
});
vm.$mount("#app")
当然,我们不需要知道vue-loader具体是怎么转换的,为什么转换后的内容可以直接被createElement()函数直接渲染。我们只需要知道vue-loader会对.vue文件中的<template></template>模板进行预编译即可。

三、自己配置webpack

为了更好的理解vue-cli生成的项目,我们可以自己配置webpack,然后实现vue-cli生成项目的运行,即自己创建一个新的项目,然后在src目录下加入main.js和App.vue以及在public目录下加入index.html文件,然后通过自己配置的webpack让项目运行起来,其webpack.config.js内容如下
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
    mode: "development",
    target: "web",
    entry: {
        main: "./src/main.js"
    },
    output: {
        path: path.resolve(__dirname, "./dist"),
        filename: "[name].js"
    },
    devServer: {
        port: 3000, // 让devServer监听3000端口
        contentBase: "./dist", // 将当前项目的dist目录作为devServer的根目录
        progress: true, // 显示打包进度条
        compress: true // 是否启用Gzip压缩,默认为false
    },
    module: {
        rules: [
            {
                test: /\.vue$/, // 处理.vue文件
                use: [
                    {
                        loader: "vue-loader"
                    }
                ]
            },
            {
                test: /\.css$/, // 处理.css文件,包括.vue文件中的<style></style>部分
                use: [
                  'style-loader',
                  'css-loader'
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ // 将打包后的js文件自动注入到public目录下的index.html文件中
            template: "./public/index.html",
            filename: "index.html"
        }),
        new VueLoaderPlugin() // 处理.vue文件
    ]
}

JS_Even_JS
2.6k 声望3.7k 粉丝

前端工程师