基于 Webpack 从 0 到 1 启动一个 Vue 项目

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 面试总是问 Webpack 怎么样?有什么用?有几个 loader,有几个 plugin,结果一去写业务,全都是上的 Vue CLI,create-react-app,启动配置什么的,前辈们都配好了,所以这篇文章就是教学如何基于 Webpack 从 0 到 1 启动一个 Vue 项目

面试总是问 Webpack 怎么样?有什么用?有几个 loader,有几个 plugin,结果一去写业务,全都是上的 Vue CLIcreate-react-app,启动配置什么的,前辈们都配好了

所以这篇文章就是教学如何基于 Webpack01 启动一个 Vue 项目,下面是这个项目运行效果

IMG

传统启动

如果你是刚开始接触 HTML/CSS/JavaScript 三件套开始接触的前端,那么你可能比较熟悉或者比较能接受的引入 Vue 的方式可能是使用 CDN 的方式,大概如下(下面这个是我要介绍的例子)

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
  <div id="app">
    <div>
      {{ "count:" + count }}
      <button @click="handleClick">click + 1</button>
    </div>
  </div>
</body>
<script>
  new Vue({
    data() {
      return {
        count: 0,
      }
    },
    methods: {
      handleClick() {
        this.count++;
      },
    }
  }).$mount('#app');
</script>

但如果你继续深入学习的话或者看过一些学习视频,往往他们会推荐你去使用 Vue CLI 去开发 Vue 项目,因为具有一定规模的项目是需要这类脚手架的,它集成了很多功能,但是如果让你自己扔掉 Vue CLI 你能否能够在浏览器中运行呢?因为究其本质,Vue CLI 是基于 nodenpm 运作的,接下来就教你们一步步简单运行一个 Vue 项目

Webpack 启动

npm 初始化

先找个位置并在终端(或者命令行)初始化一个项目

npm init

初始化后会有一些选项,可以直接回车全部忽略,也可以根据自己意向填写

IMG

选择完成之后

IMG

这个时候系统会创建一个 package.json 文件

IMG

接下来要做的就是安装 Vue

# 文章使用的 Vue2 运行
npm install vue@2.7.10

然后你的 package.json 会有多出一个 dependencies 属性,如下

{
  "dependencies": {
    "vue": "^2.7.10"
  }
}

所以上面的例子需要改成如下写法

// <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
import Vue from "vue"

但这种写法在 HTML 文件需要使用 module 加持,而且现在的 Vue 的位置在 node_modules,不能再从 HTML 中直接导入,所以需要将上面例子中的 JS 代码脱离,命名为 main.js

import Vue from "vue";

new Vue({
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    handleClick() {
      this.count++;
    },
  }
}).$mount('#app');

html 部分命名为 index.html

<body>
  <div id="app">
    <div>
      {{ "count:" + count }}
      <button @click="handleClick">click + 1</button>
    </div>
  </div>
</body>
<script type="text/javascript" src="./main.js" charset="utf-8"></script>

PS: 为什么脚本引入放在了下面?是因为 main.js 里面包含了获取 DOM 的代码,所以为了能够获取到 <div id="app">...</div> 必须放在其下面导入

此时的文件结构

├── node_modules
├── main.js
├── index.html
├── package-lock.json
└── package.json

如果你有过 HTML 部署的经验或者你的电脑上恰好有 Live Server 这个软件,你会发现这个文件结构已经能够运行

但是效果和预期不一样,会是下面这样

IMG

为什么显示是 {{"count:" + count}} 而没有被解析呢?

这是因为在 js 中默认导出的 Vue.js 是只包含运行时而不包含编译时的,而编译时干的就是解析的活,具体可以参考这个文档,对不同构建版本的解释 - Vue.js

CDN 使用的就是 UMD 完整版的 Vue.js,而 js 中导出的是只包含运行时的 Vue.js

对于这个问题可以使用 Vue 提供的 h() 渲染函数,像下面这样

new Vue({
  // ...
  render(h) {
    return h("div", [
      "count:" + this.count,
      h(
        "button",
        {
          on: {
            click: this.handleClick,
          },
        },
        "click + 1"
      ),
    ]);
  },
  // ...
}).$mount("#app");

等同于下面的 HTML

<div>
  {{ "count:" + count }}
  <button @click="handleClick">click + 1</button>
</div>

然后在 package.jsonhtml 的导入方式中设置为 module,也可以生成同样的效果,具体详情就不多演示,因为这涉及到另一个前端工程化的大方向,也就是 Vite 类型的打包框架

当然你也使用 vue-loader,但是使用 vue-loader 的前提需要一个前提,即 Webpack

Webpack

执行

npm install webpack webpack-dev-server webpack-cli --save-dev
  1. webpack 是一个打包器,可以将不同模块的 .js 文件打包成一个 .js 文件,就比如上面的 Vue.jsmain.js 合并成一个 .js 文件
  2. webpack-dev-server 可以监视 .js 文件内容的修改,进行热更新,具体场景比如更新 DOM 的内容,然后更新到浏览器
  3. webpack-cli 可以在终端执行 webpackwebpack-dev-server

webpack 重点在于它的配置,配置如下

const webpack = require("webpack");
const path = require("path");

module.exports = {
  mode: "development",
  devServer: {
    static: {
      directory: path.join(__dirname, "public"),
    },
    compress: true,
    port: 8080,
  },
  entry: path.join(__dirname, "./main.js"),
  output: {
    publicPath: "",
    path: path.join(__dirname, "./public"),
    filename: "bundle.js",
    clean: true,
  },
};

我认为 Webpack 的配置是一定要讲的,因为有非常多的属性有默认值,关键是它还比较重要!!

我强烈建议不要随便百度你遇到的问题,请去查看 Webpack 文档,即使它有点烂

解析如下

  1. modewebpack 的运行配置
  2. entry 是打包的入口文件,这里默认为 main.js
  3. output 是打包的出口文件,也就是打包结果

    1. path 是打包文件的存放位置,由于我们目的是运行 Vue 项目,目的不在打包而是运行,所以不需要过多关注
    2. publicPath 需要重点关注,和 webpack-dev-server 紧密相连,这个属性目的在于配置使用 webpack-dev-server 启动时打包文件的引用位置
  4. devServerwebpack-dev-server 的配置选项,等同于 Live Server

    1. port 启动一个服务在 localhost:port,你可以直接通过 http://localhost:port 访问热更新的内容
    2. static

      1. directory 是配置静态文件展示的位置的选项,可以简单理解可以直接在浏览器访问在这个位置的 index.html,如上就是 http://localhost:8080

配置完 Webpack,项目的文件也需要重新调整

  1. index.html 需要迁移至 public
  2. 里面的 .js 文件需要改成打包后的文件,即如下
<body>
<!-- ... -->
</body>
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>

然后还需要修改一下 package.json 的内容,修改如下

{
  "name": "vue-start-with-webpack",
  "version": "1.0.0",
  "description": "运行 vue 项目 demo",
  "main": "main.js",
  "scripts": {
    "start": "webpack server", // 启动 webpack-dev-server
  },
  "author": "poplink",
  "license": "MIT",
  "dependencies": {
    "vue": "^2.7.10"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.11.1"
  }
}

现在的目录结构如下

├── node_modules
├── public
|  └── index.html
├── main.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js

测试运行 npm run start,结果如下

解析如下

  1. modewebpack 的运行配置
  2. entry 是打包的入口文件,这里默认为 main.js
  3. output 是打包的出口文件,也就是打包结果

    1. path 是打包文件的存放位置,由于我们目的是运行 Vue 项目,目的不在打包而是运行,所以不需要过多关注
    2. publicPath 需要重点关注,和 webpack-dev-server 紧密相连,这个属性目的在于配置使用 webpack-dev-server 启动时打包文件的引用位置
  4. devServerwebpack-dev-server 的配置选项,等同于 Live Server

    1. port 启动一个服务在 localhost:port,你可以直接通过 http://localhost:port 访问热更新的内容
    2. static

      1. directory 是配置静态文件展示的位置的选项,可以简单理解可以直接在浏览器访问在这个位置的 index.html,如上就是 http://localhost:8080

配置完 Webpack,项目的文件也需要重新调整

  1. index.html 需要迁移至 public
  2. 里面的 .js 文件需要改成打包后的文件,即如下
<body>
<!-- ... -->
</body>
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>

然后还需要修改一下 package.json 的内容,修改如下

{
  "name": "vue-start-with-webpack",
  "version": "1.0.0",
  "description": "运行 vue 项目 demo",
  "main": "main.js",
  "scripts": {
    "start": "webpack server", // 启动 webpack-dev-server
  },
  "author": "poplink",
  "license": "MIT",
  "dependencies": {
    "vue": "^2.7.10"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.11.1"
  }
}

现在的目录结构如下

├── node_modules
├── public
|  └── index.html
├── main.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js

测试运行 npm run start,结果如下

IMG

注意,这个时候你可以观察一下目录里面并没有生成 bundle.js,但 webpack-dev-server 并不是没有打包,而是放在了内存里,只是你没看见,具体可以参考文档

Warning
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 [`devMiddleware.publicPath`](https://webpack.docschina.org/configuration/dev-server/#devserverdevmiddleware) 选项进行修改。

这也就是为什么需要强调 publicPath 的重要性,它就是指定了 bundle.js 的引用位置

对于值的取舍可以参考 Webpack 文档

vue-loader 引入

先安装 vue-loader

# 注意:vue-loader@15.9.2 适配 Vue2,不要安装最新版,有坑,运行不起来
# 注意:vue-template-compiler 需要和你的 Vue2 版本一致!
npm install vue-loader@15.9.2 vue-template-compiler@2.7.10 --save-dev

webpack.config.js 中添加

const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      }
    ],
  },
  plugins: [new VueLoaderPlugin()],
};

但到这里其实还没有结束,因为我们的 vue-loader 是针对 .vue 文件的,所以需要将 dom 相关的内容迁移至 .vue 文件里

main.js 同目录下创建 App.vue 文件,内容如下

<template>
  <div>
    {{ "count:" + count }}
    <button @click="handleClick">click + 1</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    handleClick() {
      this.count++;
    },
  },
};
</script>
<style></style>

main.js 的内容修改为

import Vue from "vue";
import App from "./App.vue";

new Vue({
  render: (h) => h(App),
}).$mount("#app");

index.html 的内容修改为

<body>
  <div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>

再次运行 npm run start 结果如下

IMG

总结

.js.vue 的操作就是这么简单,但好像也没用几个 loader

注意,这个时候你可以观察一下目录里面并没有生成 bundle.js,但 webpack-dev-server 并不是没有打包,而是放在了内存里,只是你没看见,具体可以参考文档

Warning
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 [`devMiddleware.publicPath`](https://webpack.docschina.org/configuration/dev-server/#devserverdevmiddleware) 选项进行修改。

这也就是为什么需要强调 publicPath 的重要性,它就是指定了 bundle.js 的引用位置

对于值的取舍可以参考 Webpack 文档

vue-loader 引入

先安装 vue-loader

# 注意:vue-loader@15.9.2 适配 Vue2,不要安装最新版,有坑,运行不起来
# 注意:vue-template-compiler 需要和你的 Vue2 版本一致!
npm install vue-loader@15.9.2 vue-template-compiler@2.7.10 --save-dev

webpack.config.js 中添加

const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      }
    ],
  },
  plugins: [new VueLoaderPlugin()],
};

但到这里其实还没有结束,因为我们的 vue-loader 是针对 .vue 文件的,所以需要将 dom 相关的内容迁移至 .vue 文件里

main.js 同目录下创建 App.vue 文件,内容如下

<template>
  <div>
    {{ "count:" + count }}
    <button @click="handleClick">click + 1</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    handleClick() {
      this.count++;
    },
  },
};
</script>
<style></style>

main.js 的内容修改为

import Vue from "vue";
import App from "./App.vue";

new Vue({
  render: (h) => h(App),
}).$mount("#app");

index.html 的内容修改为

<body>
  <div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>

再次运行 npm run start 结果如下

IMG

最后检查一下热更新

IMG

最后的文件目录如下,以及 github 的例子请参考 pandoralink/vue-start-with-webpack - github

├── node_modules
├── public
|  └── index.html
├── App.vue
├── main.js
├── package-lock.json
├── package.json
└── webpack.config.js

总结

刚刚接触 Vue CLI 的同学可以对比一下 Vue CLI 创建的项目和本篇文章的文件目录对应的文件比较,对应 Vue CLI 部分原理

_PS: .js.vue 的操作就是这么简单,但好像也没用几个 `loader

相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
1月前
|
JavaScript 数据可视化
vue-cli学习一:vue脚手架的 vue-cli2和vue-cli3版本 创建vue项目,vue的初始化详解
这篇文章介绍了如何使用vue-cli 2和3版本来创建Vue项目,并详细说明了两者之间的主要区别。
86 5
vue-cli学习一:vue脚手架的 vue-cli2和vue-cli3版本 创建vue项目,vue的初始化详解
|
28天前
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
144 2
|
1月前
|
JavaScript
Vue CLi脚手架创建第一个VUE项目
Vue CLi脚手架创建第一个VUE项目
35 3
|
19天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
5天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
1月前
|
JavaScript 数据可视化
vue-cli学习二:vue-cli3版本 创建vue项目后,Runtime-Compiler和Runtime-only两个模式详解;vue项目管理器;配置文件的配置在哪,以及如何配置
这篇文章详细介绍了Vue CLI 3版本创建项目时的Runtime-Compiler和Runtime-only两种模式的区别、Vue程序的运行过程、render函数的使用、eslint的关闭方法,以及Vue CLI 2和3版本配置文件的不同和脚手架3版本创建项目的配置文件配置方法。
38 3
vue-cli学习二:vue-cli3版本 创建vue项目后,Runtime-Compiler和Runtime-only两个模式详解;vue项目管理器;配置文件的配置在哪,以及如何配置
|
1月前
|
JavaScript
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
这篇文章是关于如何在webpack环境中配置Vue.js,包括安装Vue.js、解决报错、理解el与template的区别、使用SPA模式、抽离模板为对象、封装为单独的js文件、安装vue-loader时遇到的问题及解决方案,以及整个过程的总结。
71 2
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
|
20天前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
22天前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
20 7
|
18天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。