Vue&webpack入门实践
Posted qianbixin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue&webpack入门实践相关的知识,希望对你有一定的参考价值。
目录
1. 下载安装Vue
- 先安装好Node.js,因为接下来要使用其中的NPM安装Vue。
- 建议使用Vscode作为编辑器:创建一个文件夹,用Vscode打开。
- 打开Vscode的命令行窗口,输入命令
npm init -y
对文件夹进行初始化,完成后多了个package.json文件。 - 输入命令
npm install vue --save
命令安装Vue。
安装完后多了一个文件夹node_modules和package-lock.json,node_modules是NPM安装的模块的默认存放位置,打开发现其中有个Vue文件夹。
2. Vue
2.1 Vue要素
先看一个例子
<!DOCTYPE html>
<html>
<head>
<!-- 注意路径-->
<script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
数量为:<input type="text" v-model="num"><br>
<button v-on:click="increase">点我+1</button>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#app",
data:
num: 1
,
methods:
increase: function()
this.num++;
)
</script>
</body>
</html>
效果: 设置文本框中的数字,点击+1后在原来的基础上增加1。
2.1.1 创建Vue实例
创建方式如下,
el表示要关联的HTML模版的id,
data表示模版中数据的初始值,
methods定义了模板中出现的函数。
var app = new Vue(
el: "#app",
data: num: 1,
methods:
increase: function()
this.num++;
)
注意:这段代码必须写在html模版下面,否则不生效。
2.1.2 Html模版
要渲染的html模版,其id为app
对应于Vue构造函数中el的值#app
。
Vue只能渲染这个div内部的变量,方法等元素。
<div id="app">
数量为:<input type="text" v-model="num"><br>
<button v-on:click="increase">点我+1</button>
</div>
2.1.3 数据和视图
这是Vue的特性:双向绑定。
上面的例子中,点击+1按钮,触发increase方法,Vue实例中的num+1,文本框中显示的数字+1;
改变文本框中的数字,再点+1按钮,数字就会在原来基础上+1,说明修改显示内容也能修改对应的Vue实例中的变量。
这就是视图和数据的双向绑定,修改视图,数据就变;修改数据,视图也变。
2.1.4 方法
按钮通过v-on:click指令绑定了increase方法,方法的定义在Vue对象的methods属性里面。
html
<button v-on:click="increase">点我+1</button>
js
var app = new Vue(
...
methods:
increase: function()
this.num++;
)
2.1.5 生命周期
每个Vue实例在被创建时都要经过一系列的初始化过程,比如创建实例,装载模板等等。Vue为生命周期的每个状态都设置了监听函数。此处应该有个图片,但这个图片太长了,给个地址自己看去吧
使用方式如下
var vm = new Vue(
el:"#app",
data:...,
//监听函数
created()
...
)
2.2 指令
2.2.1 插值表达式
像jsp中的EL表达式一样,显示变量或者一个表达式的结果,有、
v-text
、v-html
三种指令
v-html
指令输出html文本后会自动渲染。
代码
<div id="test1">
“花括号表达式的值:”1+1<br>
“v-text”标签输出表达式的值:<span v-text="1+1"></span> <br>
“v-html”标签输出表达式的值:<span v-html="1+1"></span> <br>
“花括号输出文本内容:”hello<br>
“v-text”标签输出普通文本:<span v-text="hello"></span> <br>
“v-html”标签渲染html文本:<span v-html="hello"></span>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test1",
data:
hello: "<h1>大家好,我是小明</h1>"
)
</script>
结果
2.2.2 v-model
插值表达式只能通过改变变量来改变显示的内容,而v-model
指令可以让数据和视图相互影响。
由于视图可操作,因此限制了对应的html标签的种类。v-model
指令支持的标签有
- input
- select
- textarea
- checkbox
- radio
- components(Vue中的自定义组件)
代码
<div id="test2">
<input type="checkbox" v-model="language" value="Java" />Java<br />
<input type="checkbox" v-model="language" value="php" />PHP<br />
<input type="checkbox" v-model="language" value="Swift" />Swift<br />
<h1>
你选择了:language.join(',')
</h1>
</div>
<script type="text/javascript">
var vm = new Vue(
el: "#test2",
data:
language: []
)
</script>
结果
选择复选框后显示的内容会对应变化;后台修改数据,显示的选择项也会变化。
2.2.3 v-on
为元素绑定事件。比如绑定点击事件的语法是v-on:click
,可以简写为@click
。
<!-- 给页面元素绑定事件 -->
<div id="test3">
<!--事件中直接写js片段-->
<button v-on:click="num++">增加</button><br />
<!--事件指定一个回调函数,必须是Vue实例中定义的函数-->
<button v-on:click="decrement">减少</button><br />
<!-- 简写方式 -->
<button @click="decrement">减少</button><br />
<!--键盘事件-->
<button @keyup.enter="num++">先使我获得焦点,然后按下enter键增加</button><br />
<h1>num: num</h1>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test3",
data:
num: 1
,
methods:
decrement()
this.num--;
)
</script>
2.2.4 v-for
用于循环遍历数据
遍历数组
<!-- 遍历数组 -->
<div id="test4">
<ul>
<li v-for="(user,index) in users">
index - user.name : user.gender : user.age
</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue(
el: "#test4",
data:
users: [
name: '小红',
gender: '女',
age: 11
,
name: '小明',
gender: '男',
age: 10
]
)
</script>
遍历对象
<div id="test5">
<ul>
<li v-for="(value,key,index) in user">
index - key : value
</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue(
el: "#test5",
data:
user:
name: '小明',
gender: '男',
age: 10
)
</script>
2.2.5 v-if和v-show
v-if用于条件判断
<div id="test6">
<!--事件中直接写js片段-->
<button v-on:click="show = !show">点击切换</button><br />
<h1 v-if="show">
你好
</h1>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test6",
data:
show: true
)
</script>
点击按钮,文本内容会隐藏。
v-if也可以和v-for结合使用
<div id="test7">
<ul>
<li v-for="(user,index) in users" v-if="user.gender === '女'">
index - user.name : user.gender : user.age
</li>
</ul>
</div>
<script type="text/javascript">
var vm = new Vue(
el: "#test7",
data:
users: [
name: '小红',
gender: '女',
age: 10
,
name: '小明',
gender: '男',
age: 10
]
)
</script>
结果只显示一个小红
有if当然也有else和elseif
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
v-show控制元素的显示,如果元素隐藏了,并不是从dom中移除,而是设置display为none
<div id="test6">
<!--事件中直接写js片段-->
<button v-on:click="show = !show">点击切换</button><br />
<h1 v-show="show">
你好
</h1>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test6",
data:
show: true
)
</script>
2.2.6 v-bind
假如我们想改变class属性,这样写是错误的
<div class="isAcctive"></div>
因为插值表达式不能用于属性的值中。
我们可以借助v-bind:class
指令,可以简写为:class
<div id="test8">
<div :class="isActive">注意class的值</div>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test8",
data:
isActive: ['active', 'hasError']
)
</script>
结果
2.2.7 计算属性
在插值表达式中写一长串代码很难看,可以用“计算属性”来替代,这个属性实际上是个方法,将方法名以变量形式绑定到插值表达式中了:
<div id="test9">
<h1>您的生日是:birth </h1>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test9",
data:
birthday: 1429032123201 // 毫秒值
,
computed:
birth() // 计算属性本质是一个方法,但是必须返回结果
const d = new Date(this.birthday);
return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDay();
)
</script>
结果
2.2.8 watch
假如我们要监控用户输入了什么内容,可以用监控属性来实现
<!-- 监控属性变化 -->
<div id="test10">
<input type="text" v-model="message">
</div>
<script type="text/javascript">
var vm = new Vue(
el: "#test10",
data:
message: ""
,
watch:
message(newVal, oldVal)
console.log(newVal, oldVal);
)
</script>
结果
在文本框输入good,控制台记录了每个按键按下的动作
2.3 组件化
一个页面中总有相同的部分可以抽取出来做为组件使用,Vue提供了定义组件的方法。
2.3.1 全局组件
代码
<div id="test1">
<counter></counter>
</div>
<script type="text/javascript">
// 定义全局组件,参数1,组件名称;参数2,组件参数。
// 用html模板替换掉了counter标签,注意数据传递的方式,是data()而不是data
Vue.component("counter",
template: '<button v-on:click="count++">你点了我 count 次</button>',
data()
return
count: 0
)
var app = new Vue(
el: "#test1"
);
</script>
页面表现
- 组件也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
- 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
- 但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
- 全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
- data的定义方式比较特殊,必须是一个函数。
定义全局组件后,可以在其他地方使用。
值得注意的一点:使用者必须也是一个Vue实例。
<div id="test2">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script type="text/javascript">
var app = new Vue(
el: "#test2"
);
</script>
现象
可以看出:每个组件都维护自己的一份变量。
2.3.2 局部组件
就像定义变量一样定义了一个局部组件localCounter
,将其注册到我们自定义的<test-component></test-component>
标签中。
注意:此处用了驼峰命名法来对应标签:testComponent对应于test-component
<div id="test3">
<test-component></test-component>
</div>
<script type="text/javascript">
const localCounter =
template:
'<button v-on:click="count++">我是局部组件:你点了我 count 次</button>',
data()
return
count: 0
;
var app = new Vue(
el: "#test3",
components:
testComponent: localCounter
);
</script>
2.3.3 组件通信
2.3.3.1 父向子通信
下面的例子展示了:父组件得到lessons的数据后传递到子组件中,以子组件的模板显示
<div id="test4">
<parent :items="lessons" />
</div>
<script type="text/javascript">
//子组件,通过props来接收父组件传过来的数据
//这个子组件是一个局部组件
const son =
template:
'<ul> <li v-for="item in items" :key="item.id">item.id : item.name</li> </ul>',
props:
items:
type: Array,
default: []
var app = new Vue(
el: "#test4",
components:
parent: son
,
data:
lessons: [
id: 1,name: 'java',
id: 2,name: 'php',
id: 3,name: 'ios',
]
)
</script>
现象
2.3.3.2 子向父通信
代码
<div id="test5">
<h2>num: num</h2>
<my-component @inc="increment" @dec="decrement"></my-component>
</div>
<script type="text/javascript">
Vue.component("myComponent",
template: ' <div> <button @click="plus">加</button> <button @click="reduce">减</button> </div>',
methods:
plus()
this.$emit("inc");
,
reduce()
this.$emit("dec");
)
var app = new Vue(
el: "#test5",
data:
num: 0
,
methods:
increment()
this.num++;
,
decrement()
this.num--;
);
</script>
现象
点击加按钮,数值增加;点击减按钮,数量减少。
解释
父组件是
<my-component @inc="increment" @dec="decrement"></my-component>
子组件是
Vue.component("myComponent",
template: ' <div> <button @click="plus">加</button> <button @click="reduce">减</button> </div>',
methods:
plus()
this.$emit("inc");
,
reduce()
this.$emit("dec");
)
想要通过子组件中的加和减按钮实现num的加和减,就要调用父组件的函数。
$emit()
方法是Vue提供的用于调取父组件绑定函数的函数。
最后流程就是:点击加按钮,调用子组件的plus方法,plus方法调取父组件的increment方法实现num的增加操作。
2.4 vue-router
2.4.1 安装vue-router
安装命令npm install vue-router --save
引入依赖
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
2.4.2 实现功能
利用vue-router实现登录注册菜单的切换:
点击登录后显示登录页
点击注册后显示注册页
将登录、注册和路由分成三个组件,由index.html引入并使用。目录结构如下
login.js
const loginForm =
template:' <div> <h2>登录页</h2> 用户名:<input type="text"><br/> 密码:<input type="password"><br/> </div> '
register.js
const registerForm =
template:' <div> <h2>注册页</h2> 用户名:<input type="text"><br/> 密码:<input type="password"><br/> 确认密码:<input type="password"><br/> </div> '
router.js
// 根据路径渲染对应的组件
const indexRouter = new VueRouter(
routes:[
path:"/login",
component: loginForm
,
path: "/register",
component: registerForm
]
)
index.html
<!DOCTYPE html>
<html>
<head>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<!-- 跳转链接,点击后将地址传送给router -->
<span><router-link to="/login">登录</router-link></span>
<span><router-link to="/register">注册</router-link></span>
<hr/>
<div>
<!-- 路由锚点,点击跳转链接后router会在此渲染对应组件的html模板-->
<router-view></router-view>
</div>
</div>
<script src="js/login.js"></script>
<script src="js/register.js"></script>
<script src="js/router.js"></script>
<script type="text/javascript">
var vm = new Vue(
el: "#app",
router: indexRouter //使用路由
)
</script>
</body>
</html>
3. webpack
3.1 webpack简介
Webpack 是一个前端资源的打包工具,它可以将js、image、css等资源打包成一个整体,压缩混淆提高性能和安全性,将高级语法转义,提高兼容性。
3.2 四个核心概念
入口(entry)
webpack打包的启点,可以有一个或多个,一般是js文件。webpack会从启点文件开始,寻找启点直接或间接依赖的其它所有的依赖,包括JS、CSS、图片资源等,作为将来打包的原始数据。
输出(output)
出口一般包含两个属性:path和filename。用来告诉webpack打包的目标文件夹,以及文件的名称。目的地也可以有多个。
加载器(loader)
webpack本身只识别Js文件,如果要加载非JS文件,必须指定一些额外的加载器(loader),例如css-loader。然后将这些文件转为webpack能处理的有效模块,最后利用webpack的打包能力去处理。
插件(plugins)
插件可以扩展webpack的功能,让webpack不仅仅是完成打包,甚至各种更复杂的功能,或者是对打包功能进行优化、压缩,提高效率。
3.3 安装
执行命令npm install webpack webpack-cli --save-dev
在package.json里多了几行配置,分别是运行时依赖和开发依赖
3.4 配置
先在package.json同级的目录下新建webpack.config.js文件。
接下来以vue-router的demo为例演示webpack的用法。
3.4.1 配置入口
入口一般是一个或多个js文件,通过这些js文件找出相关的所有依赖树,最后打包输出。
在vue-router的demo里面,index.html引用了相关的js,但它是html文件,不适合作为启点。
因此我们需要一个额外的main.js引入其他所有的js。
main.js
//ES6语法导入js模块,注意:不带.js后缀
import Vue from '../node_modules/vue/dist/vue'
import VueRouter from '../node_modules/vue-router/dist/vue-router'
import loginForm from './js/login'
import registerForm from './js/register'
//VueRouter使用模块加载,必须加上这一句
Vue.use(VueRouter)
const indexRouter = new VueRouter(
routes:[
path:"/login",
component: loginForm
,
path: "/register",
component: registerForm
]
)
var vm = new Vue(
el: "#app",
router: indexRouter
)
loginForm和registerForm都是做为js模块导入的,他们自己也要分别追加导出语句。
在login.js中追加一句
export default loginForm;
在register.js中追加一句
export default registerForm;
最后配置入口:在webpack.config.js中指定入口文件
module.exports=
entry:'./src/main.js', //指定打包的入口文件
3.4.2 配置出口
在webpack.config.js中指定出口路径和最终输出的文件名,最终的js如下
module.exports=
entry:'./src/main.js', //指定打包的入口文件
output:
// path: 输出的目录,__dirname是相对于webpack.config.js配置文件的绝对路径
path : __dirname+'/dist',
filename:'build.js' //输出的js文件名
3.5 执行打包并测试运行
执行命令npx webpack --config webpack.config.js
去掉index.html中的js引用标签,只引用打包好的build.js
<script src="../dist/build.js"></script>
测试运行,结果与打包之前一样。
3.6 打包CSS
webpack的四个核心概念,我们目前只使用了两个:入口和出口。
接下来使用加载器,加载非js资源,比如CSS。
3.6.1 安装加载器
执行命令:npm install style-loader css-loader --save-dev
3.6.2 编写css
新建一个和js目录同级的css文件夹,里面新建main.css
#app a
display: inline-block;
width: 150px;
line-height: 30px;
background-color: dodgerblue;
color: white;
font-size: 16px;
text-decoration: none;
#app a:hover
background-color: whitesmoke;
color: dodgerblue;
#app div
width: 300px;
height: 150px;
#app
width: 400px;
border: 1px solid dodgerblue;
3.6.3 引入css模块
在main.js中添加import语句
import './css/main.css'
3.6.4 配置加载器
在webpack.config.js里配置,最终如下所示
module.exports=
entry:'./src/main.js', //指定打包的入口文件
output:
// path: 输出的目录,__dirname是相对于webpack.config.js配置文件的绝对路径
path : __dirname+'/dist',
filename:'build.js' //输出的js文件名
,
module:
rules: [
test: /\\.css$/, // 通过正则表达式匹配所有以.css后缀的文件
use: [ // 要使用的加载器,这两个顺序一定不要乱
'style-loader',
'css-loader'
]
]
3.6.5 重新打包并运行
依然是执行npx webpack --config webpack.config.js
最终的CSS效果能够展示出来(不贴图片了)。
注意:所有的js、css最终都被放到了build.js里面。
3.7 快速执行打包命令
package.json里面可以配置脚本,在scripts标签里面添加"build": "webpack"
"name": "vue-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts":
"test": "echo \\"Error: no test specified\\" && exit 1",
"build": "webpack"
,
"keywords": [],
"author": "",
"license": "ISC",
"dependencies":
"vue": "^2.6.10",
"vue-router": "^3.0.6"
,
"devDependencies":
"css-loader": "^2.1.1",
"style-loader": "^0.23.1",
"webpack": "^4.33.0",
"webpack-cli": "^3.3.3"
这样就可以执行npm run build
命令进行打包,代替原先的npx webpack --config webpack.config.js
命令。
3.8 打包html
之前都是将js、css等相关文件打包好,最后手动引入到index.html中。
而webpack的html打包插件,可以把html和js、css等一起打包直接生成可用的页面。
3.8.1 安装并配置插件
执行npm install --save-dev html-webpack-plugin
命令安装
配置webpack.config.js,最终如下
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports=
entry:'./src/main.js', //指定打包的入口文件
output:
// path: 输出的目录,__dirname是相对于webpack.config.js配置文件的绝对路径
path : __dirname+'/dist',
filename:'build.js' //输出的js文件名
,
module:
rules: [
test: /\\.css$/, // 通过正则表达式匹配所有以.css后缀的文件
use: [ // 要使用的加载器,这两个顺序一定不要乱
'style-loader',
'css-loader'
]
]
,
plugins:[
new HtmlWebpackPlugin(
title: '首页', //生成的页面标题<head><title>首页</title></head>
filename: 'index.html', // dist目录下生成的文件名
template: './src/index.html' // 我们原来的index.html,作为模板
)
]
3.8.2 打包查看效果
去掉原先index.html里面的引用build.js的标签
执行打包命令npm run build
发现dist文件夹下多了个index.html,运行发现效果与之前一致。
3.9 热更新
每次修改js或css都要重新打包,麻烦,webpack提供了一个热更新插件,这个插件搞了一个服务器,
我们修改代码后,可以自动检测修改并更新。
安装命令是:npm install webpack-dev-server --save-dev
之后在package.json中的scripts标签下添加
"dev": "webpack-dev-server --inline --hot --open --port 8081 --host 127.0.0.1"
注意端口号。
执行命令npm run dev
,注意:是dev,不是build。
4. Vue-cli
使用webpack打包vue项目时需要我们手动安装插件、配置等,很麻烦。Vue提供了一个脚手架,Vue-cli,能够快速搭建一个web项目模板。
4.1 安装
执行命令npm install -g vue-cli
初始化webpackvue init webpack
,接下来这样选择
4.2 文件结构分析
- src/main.js
项目的入口文件
- src/App.vue
文件内容如下
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view/>
</div>
</template>
<script>
export default
name: 'App'
</script>
<style>
#app
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
</style>
*.vue文件叫做单文件组件,每一个.vue文件都是一个独立的vue组件。
里面包含三部分内容
template:模板,支持html语法高亮和提示
script:js脚本,这里编写的就是vue的组件对象,看到上面的data()了吧
style:样式,支持CSS语法高亮和提示
每个组件都有自己独立的html、JS、CSS,互不干扰,真正做到可独立复用。
- package.json
"name": "vue-cli-test",
"version": "1.0.0",
"description": "",
"author": "",
"private": true,
"scripts":
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
,
"dependencies":
"vue": "^2.5.2",
"vue-router": "^3.0.1"
,
"devDependencies":
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
,
"engines":
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
,
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
- 可以看到这引入了非常多的依赖,绝大多数都是开发期依赖,比如大量的加载器。
- 运行时依赖只有vue和vue-router
- 脚本有三个:
- dev:使用了webpack-dev-server命令,开发时热部署使用
- start:使用了npm run dev命令,与上面的dev效果完全一样
- build:等同于webpack的打包功能,会打包到dist目录下。
执行npm run dev
或者 npm start
都可以启动项目。
以上是关于Vue&webpack入门实践的主要内容,如果未能解决你的问题,请参考以下文章
关于webpack下热更新?&自动刷新?的小记(非vue-cli)