完整原文地址见简书
更多完整Vue笔记目录敬请见《前端 Web 笔记 汇总目录(Updating)》
本文内容提要
VueCli部分
首先需要安装nodejs
- 安装完node会自动配套npm
- 使用
npm install nrm -g
用于调整 镜像源,方便后续下载依赖- 这边使用淘宝镜像
npm uninstall vue-cli -g
检查并清除 多余的旧版本- 使用
npm install -g @vue/cli[@版本号]
安装 脚手架- 使用 脚手架 Vue Cli,从 创建项目 到 运行项目 的过程
- 退出之后,把刚刚创建的项目拉进VSCode,使用VSCode启动项目
- 初始项目结构解读
- 源代码在
src
下,main.js
是入口App.vue
文件 简读- 关于
HelloWorld.vue
文件- 单文件组件 的含义
- 基于工程实现TODOList案例 --- 单组件版[App.vue]
- 基于工程实现TODOList案例 --- 父子组件版[App.vue、ListItem.vue]
- Vue-Router部分
- 在代码中使用Router
- Router的作用 及 简述
- 首先看一下App.vue根组件怎么写
- 解析一下这个多出来的
router/index.js
文件- view目录下的文件
- 例程,拓展一个Router页面
- 补充:Router路由懒加载语法糖 简述 与例程实战
- VueX部分
- 首先需要创建项目
- --- 特性配置:
- package.json文件
- VueX简述
- VueX 框架的引入、数据的定义 以及 在组件中的使用
- 在Home.vue中 使用这个 VueX提供的 全局数据字段:
- 如何在任一组件中 修改 VueX的 数据
- VueX的异步操作 同步操作
- 带参数地 修改VueX数据
- VueX修改数据 流程设计的理解
- 安装、使用axios发送ajax请求
- 把上例的axios请求 封装到 actions中
VueCli部分
首先需要安装nodejs
参考博客:
--- Install Node.js
--- Node.js 安装配置
安装完node会自动配套npm
使用npm install nrm -g
用于调整 镜像源,方便后续下载依赖
安装完了注意,
如C:\Users\凌川江雪\AppData\Roaming\npm\nrm -> C:\Users\凌川江雪\AppData\Roaming\npm\node_modules\nrm\cli.js
乃是依赖的安装代码路径;
nrm ls
可以切换镜像源:
安装后使用时,你可能遇到这个问题:
D:\OK\nodejsOther>nrm ls internal/validators.js:124 throw new ERR_INVALID_ARG_TYPE(name, 'string', value); ^ [TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type >string. Received undefined at validateString (internal/validators.js:124:11) at Object.join (path.js:375:7) at Object.<anonymous> (C:\Users\凌川江雪>\AppData\Roaming\npm\node_modules\nrm\cli.js:17:20) at Module._compile (internal/modules/cjs/loader.js:1063:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10) at Module.load (internal/modules/cjs/loader.js:928:32) at Function.Module._load (internal/modules/cjs/loader.js:769:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) at internal/main/run_main_module.js:17:47 ] { code: 'ERR_INVALID_ARG_TYPE' }
这边使用淘宝镜像
npm uninstall vue-cli -g
检查并清除 多余的旧版本
使用npm install -g @vue/cli[@版本号]
安装 脚手架
脚手架沉淀了许多最佳实践,
可以借助它快速生成Vue工程,包括 项目目录组织、webpack打包配置等;
使用 脚手架 Vue Cli,从 创建项目 到 运行项目 的过程
命令:vue create [项目名]
如vue create demo-pro
;
运行创建命令之后,工具会询问创建方式:
接着,进入选择特性界面:
这里选择以上三个特性即可,然后回车:
回车确定;
这里先选择第一个,回车确定;
config文件
,放一个单独的文件
里,还是放一个package.json
里,这里先选第一个;
回车后工程开始创建:
工程创建完成:
使用
npm run serve
启动工程:ctrl + c
两次可以终止运行:退出之后,把刚刚创建的项目拉进VSCode,使用VSCode启动项目
因为我们无需每次都用cmd
去启动项目;
【刚拉进来可能启动不了,报9009之类的错,
这时候重启一下VSCode就是了;
如果项目中没有
node_modules
,则需先运行
npm install
安装node_modules
依赖!!】初始项目结构解读
注意要在VS code中安装
vetur
这个插件,
使得VS可以提供 语法高亮、提示 等效果:
源代码在src
下,main.js是入口
--- import { createApp } from 'vue'
指明createApp
的来源;
--- import App from './App.vue'
指明App
实例,来自于当前文件夹下的 App.vue
文件;
--- createApp(App).mount('#app')
则
创建实例、挂载实例:
App.vue
文件 简读
--- <template>标签对的内容,
其实就等价于之前在组件实例中写的template:
键模板;
--- <script>和<style>标签对自然就是js和样式的“根据地了”;
--- 其中<script>
中的 name
指定了根组件实例名,
component
这里引入了一个子组件
HelloWorld,
子组件从import HelloWorld from './components/HelloWorld.vue'
,
可以看出其定义的地方,即 components
文件夹目录下的HelloWorld.vue
;
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</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>
关于HelloWorld.vue
文件
看了一下结构,其实也没什么特殊的了,
跟上面App.vue
的结构大体都是一样的:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
单文件组件 的含义
顾名思义,即一个组件就代表了一个组件,
如上的App.vue
、HelloWorld.vue
都是单文件组件;
单独一个文件内容,就是完整的 HTML(<template>) + CSS(<style>) + JS(<script>)
结构了;
基于工程实现TODOList案例 --- 单组件版[App.vue]
<template>
<div>
<input v-model="inputValue" />
<button class="button" @click="handleAddItem">提交</button>
</div>
<ul>
<li v-for="(item, index) in list" :key="index">
{{ item }}
</li>
</ul>
</template>
<script>
import { reactive, ref
} from "vue";
export default {
name: "App",
setup() {
const inputValue = ref("");
const list = reactive([]);
const handleAddItem = () => {
list.push(inputValue.value);
inputValue.value = "";
};
return { list, inputValue, handleAddItem };
},
};
</script>
<style>
.button {
margin-left: 20px;
}
</style>
运行效果:
基于工程实现TODOList案例 --- 父子组件版[App.vue、ListItem.vue]
首先需要创建一个子组件单文件:
其代码:
<template>
<div class="hello">
<li>{{ msg }}</li>
</div>
</template>
<script>
export default {
name: 'ListItem',
props: {
msg: String
}
}
</script>
<style>
</style>
App.vue:
与上例 主要区别就是在<script>中引入,
在<template>中修改:
<template>
<div>
<input v-model="inputValue" />
<button class="button" @click="handleAddItem">提交</button>
</div>
<ul>
<list-item v-for="(item, index) in list" :key="index" :msg="item" />
</ul>
</template>
<script>
import { reactive, ref } from "vue";
import ListItem from "./components/ListItem.vue";
export default {
name: "App",
components: { ListItemListItem },
setup() {
const inputValue = ref("");
const list = reactive([]);
const handleAddItem = () => {
list.push(inputValue.value);
inputValue.value = "";
};
return { list, inputValue, handleAddItem };
},
};
</script>
<style>
.button {
margin-left: 20px;
}
</style>
运行效果同上例;
Vue-Router部分
创建带router的项目
选择特性的时候要选上Router:
适配会简单些:
在代码中使用Router
创建好工程项目后,同样把它拉到VScode里面,
可以看到这里的目录,
可以看到main.js中,多了一个.use(router)
Router的作用 及 简述
作用:根据url的不同,展示不同的内容;
如下,运行项目,默认urlhttp://localhost:8080/#/
,展示主页(Home页):
http://localhost:8080/#/about
访问,则展示about页:首先看一下App.vue根组件怎么写
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</template>
<style>
</style>
---
router-link
是定义 跳转路由的标签,
to属性
可以配置url尾部参数
【前部 自动补上 网站根地址】,
标签内容
配置显示的内容;
点击标签内容
,即跳转到,to
补全url 指向的页面!!
如果有写<router-view/>
,则不跳转,乃显示在<router-view/>
中;
---<router-view/>
则是
根据router-link
以及网页url
组成的url路由
,
在router/index.js
文件中的路由对象
(如下一节的routes
)里,
找到对应的组件路由属性
,拿到对应的组件文件路径
,
在view
目录中找到 对应的组件 去显示!
解析一下这个多出来的 router/index.js
文件
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
--- createRouter是vue-router的一个函数,用于创建和初始化Router;
同时这里第二个参数 使用了路由参数routes
;--- 注意 定义routes参数这里,
path
定义 路径、name
定义 名称、component
进行 组件的引入;
routers
里的组件既称之为component
,
也称之为view
,子组件的单文件都放在view
文件夹下;
view目录下的文件
-- 可以看到Home.vue这里其实引用一个HelloWorld子组件:
例程,拓展一个Router页面
-
首先App.vue添加 router-link:
-
views目录下创建 单文件组件:
-
router/index.js 的Object Array中,定义一个对应的路由元素:
完事,运行,点击Heheda,效果:
补充:Router路由懒加载语法糖 简述 与例程实战
如上例程中,router/index.js
中的这个写法,
component
这里使用了 import
的方式 引入了组件,
这是一种懒加载、异步加载
(如模板注释:lazy-loaded
)的方式,
即当网页跳到这一页的时候,才会加载对应的资源文件,否则不加载;
而如 Home页的加载方式,
则是普通的常规加载:
所以,
--- 异步加载的方式:
首页打开会快点,节省不必要的资源占用,
但是在切换到懒加载
页面时,则需要花费一定的额外加载时间;
--- 同步加载的默认方式:
则可能 一开始打开首页等页面 会慢一些,
但是会把其他页面一开始就加载好,切换的时候会快一点;
--- 具体选择哪种方式,就根据业务需要进行选择;
...
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/heheda',
name: 'Heheda',
component: () => import(/* webpackChunkName: "about" */ '../views/Heheda.vue')
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
...
试验,运行上个例程,之后打开浏览器测试工具:
可以看到这个时候页面才加载about的资源:
VueX部分
首先需要创建项目
--- 特性配置:
package.json文件
VueX简述
VueX 其实就是一个
数据管理框架
,
它创建了一个全局的、唯一的数据仓库
;
当一个前端项目特别大的时候,
或者类似 几十个页面 同步共享 某部分数据 的场景,
我们不可能还是用props、provide、inject等语法去传递数据,
这个时候我们需要一个更加完善的数据管理方案;
VueX 框架的引入、数据的定义 以及 在组件中的使用
main.js中use它:
这里在state中准备了一个测试数据:
在Home.vue中 使用这个 VueX提供的 全局数据字段:
这里借助
computed
属性,通过
this.$store.state.myTestString
获取到 数据字段:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<h1>{{myTestString}}</h1>
</div>
</template>
<script>
// @ is an alias to /src
export default {
name: 'Home',
computed: {
myTestString() {
return this.$store.state.myTestString;
}
}
}
</script>
运行效果:
<template>
<div class="about">
<h1>This is an about page</h1>
<h1>{{myTestString}}</h1>
</div>
</template>
<script>
export default {
name: 'About',
computed: {
myTestString() {
return this.$store.state.myTestString;
}
}
}
</script>
运行效果:如何在任一组件中 修改 VueX的 数据
流程总结:
要修改数据的组件,
发起dispatch(事件)
--->
store/index.js
中actions
里
对dispatch
的事件 进行 监听 和回调处理,
然后发起一个commit(事件)
--->
store/index.js
中mutations
里
对commit
的事件 进行 监听 和回调处理,
处理逻辑中,完成对数据的修改;
--- 首先,需要在事件触发的函数里,
派发一个action,
改变数据 这里在About.vue中,
我们派testChange
【这玩意是可以自定义的】的action,
this.$store.dispatch("testChange");
<template>
<div class="about">
<h1 @click="handleClick">This is an about page</h1>
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
export default {
name: "About",
computed: {
myTestString() {
return this.$store.state.myTestString;
},
},
methods: {
handleClick() {
this.$store.dispatch("testChange");
},
},
};
</script>
--- 接着在store/index.js
中actions
里,即这个VueX全局数据仓库中,
做dispatch
的 监听回调处理,
store/index.js
中的actions
会响应任意组件的dispatch
;
--- 再接着,
在actions
里 对应的回调方法中,使用commit('自定义事件名')
,
触发一个mutations
,
store/index.js
中的mutations
,
会响应actions
的commit
;
--- 最后,
在store/index.js
中的mutations
里,
做actions
的commit
的监听回调,
在对应commit
的 事件回调函数中(如testChange()
),
修改数据(如this.state.myTestString = "lueluelue";
)即可;
import { createStore } from 'vuex'
export default createStore({
state: {
myTestString: "heheda",
},
mutations: {
testChange() {
console.log("mutations --- testChange");
this.state.myTestString = "lueluelue";
}
},
actions: {
testChange() {
console.log("actions --- testChange");
this.commit('testChange');
}
},
modules: {
}
})
运行:
可见完成了数据的修改,效果:
可见这边的数据也跟着改变了,
体现了VueX的
全局特性
:以上是比较完整的步骤,而如果修改数据的时候不涉及异步操作,则可以简化上述流程
即省略组件的dispatch
和store的actions
的步骤,
组件直接就commit,
然后回调到store的mutations,
直接修改数据:
VueX的异步操作 同步操作
VueX建议在
mutations
中只进行立即执行的同步操作
,
如果要进行异步操作
,必须要在actions
中进行,
也就是要采用上上节的步骤 进行VueX数据的修改;
例程,首先需要组件发起dispatch:
接着在actions中进行异步操作:
import { createStore } from 'vuex'
export default createStore({
state: {
myTestString: "heheda",
},
mutations: {
testChange() {
console.log("mutations --- testChange");
this.state.myTestString = "lueluelue";
}
},
actions: {
testChange() {
setTimeout(() => {
console.log("actions --- testChange");
this.commit('testChange');
}, 2000);
}
},
modules: {
}
})
运行,点击文本,两秒后文本(即背后的数据)自动改变:
带参数地 修改VueX数据
--- About.vue
dispatch时,
传递的 第一个参数为action
,
第二个参数为意图改动的目标数据参数
:
<template>
<div class="about">
<h1 @click="handleClick">This is an about page</h1>
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
export default {
name: "About",
computed: {
myTestString() {
return this.$store.state.myTestString;
},
},
methods: {
handleClick() {
this.$store.dispatch("testChange", "xixixihehehe");
},
},
};
</script>
--- store/index.js:
--- actions中的 事件回调函数,自动生成两个形参,
第一个为store实例,
第二个为 组件中dispatch 传递过来的 数据参数;
--- mutations的 事件回调函数,也自动生成两个形参,
第一个为 state实例,
它的值是 以Proxy的结构存储着
回调当前事件处理函数
的时刻 store 数据仓库的 状态【即 state属性】,
第二个为 actions中commit 【同步操作时,也可以是组件中的commit】
传递过来的 数据参数;
import { createStore } from 'vuex'
export default createStore({
state: {
myTestString: "heheda",
},
mutations: {
testChange(state, str) {
console.log("mutations --- testChange");
console.log("mutations --- testChange --- state", state);
state.myTestString = str;
}
},
actions: {
testChange(store, str) {
setTimeout(() => {
console.log("actions --- testChange");
console.log("actions --- testChange --- store", store);
// this.commit('testChange');
store.commit('testChange', str);
}, 2000);
}
},
modules: {
}
})
运行,点击文本,
两秒后字体改变,效果:
VueX修改数据 流程设计的理解
这样设计,
--- 可以把同步操作的逻辑封装在mutations
中处理,
把异步操作的逻辑封装在actions
中处理;
--- 又可以通过对触发事件名
的自定义
,
对特定的业务处理逻辑、修改数据代码块 做标记;
--- 如此使得项目可维护性高、可拓展性高、可读性高,
出问题时容易排查,拓展代码时也比较方便;
在setup【compositionAPI】中使用VueX
--- Home.vue:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
import { useStore } from "vuex";
export default {
name: "Home",
setup() {
const store = useStore();
const myTestString = store.state.myTestString;
return { myTestString };
},
};
</script>
运行效果:使用toRefs整理上述代码
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
import { toRefs } from "vue";
import { useStore } from "vuex";
export default {
name: "Home",
setup() {
const store = useStore();
const { myTestString } = toRefs(store.state);
return { myTestString };
},
};
</script>
运行效果同上例;
在About页中 试试 setup中修改数据
--- About.vue
<template>
<div class="about">
<h1 @click="handleClick">This is an about page</h1>
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
import { toRefs } from "vue";
import { useStore } from "vuex";
export default {
name: "About",
setup() {
const store = useStore();
const { myTestString } = toRefs(store.state);
const handleClick = () => {
store.commit("testChange", "xixixihehehe");
};
return { myTestString, handleClick };
}
};
</script>
--- store/index.js:
import { createStore } from 'vuex'
export default createStore({
state: {
myTestString: "heheda",
},
mutations: {
testChange(state, str) {
console.log("mutations --- testChange");
console.log("mutations --- testChange --- state", state);
state.myTestString = str;
}
},
modules: {
}
})
运行,到About页,
点击文本:
试一下 setup异步处理
--- About.vue
<template>
<div class="about">
<h1 @click="handleClick">This is an about page</h1>
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
import { toRefs } from "vue";
import { useStore } from "vuex";
export default {
name: "About",
setup() {
const store = useStore();
const { myTestString } = toRefs(store.state);
const handleClick = () => {
store.dispatch("testChange", "xixixihehehe");
};
return { myTestString, handleClick };
}
};
</script>
--- store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
myTestString: "heheda",
},
mutations: {
testChange(state, str) {
console.log("mutations --- testChange");
console.log("mutations --- testChange --- state", state);
state.myTestString = str;
}
},
actions: {
testChange(store, str) {
setTimeout(() => {
console.log("actions --- testChange");
console.log("actions --- testChange --- store", store);
// this.commit('testChange');
store.commit('testChange', str);
}, 2000);
}
},
modules: {
}
})
使用axios发送ajax请求
-
首先需要安装axios:
找到一个fastmock接口,
https://www.fastmock.site/mock/ae8e9031947a302fed5f92425995aa19/jd/api/user/register;
其内容:
在About.vue中请求数据并显示:
--- 主要注意要
import
;--- get方法的参数为url,访问数据接口;
--- then接收 接口回复;
<template>
<div class="about">
<h1 @click="handleClick">This is an about page</h1>
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
import { toRefs } from "vue";
import { useStore } from "vuex";
import axios from "axios";
export default {
name: "About",
setup() {
axios
.get(
"https://www.fastmock.site/mock/ae8e9031947a302fed5f92425995aa19/jd/api/user/register"
)
.then((response) => {
console.log("response", response);
const msg = response.data.desc;
console.log("response.data.desc", msg);
});
const store = useStore();
const { myTestString } = toRefs(store.state);
const handleClick = () => {
store.dispatch("testChange", "xixixihehehe");
};
return { myTestString, handleClick };
},
};
</script>
运行效果:
把上例的axios请求 封装到 actions中
--- About.vue
<template>
<div class="about">
<h1 @click="handleClick">This is an about page</h1>
<h1>{{ myTestString }}</h1>
</div>
</template>
<script>
import { toRefs } from "vue";
import { useStore } from "vuex";
export default {
name: "About",
setup() {
const store = useStore();
const { myTestString } = toRefs(store.state);
const handleClick = () => {
store.dispatch("testChange", "xixixihehehe");
};
return { myTestString, handleClick };
},
};
</script>
--- store/index.js
import { createStore } from 'vuex'
import axios from "axios";
export default createStore({
state: {
myTestString: "heheda",
},
mutations: {
testChange(state, str) {
console.log("mutations --- testChange");
console.log("mutations --- testChange --- state", state);
state.myTestString = str;
}
},
actions: {
testChange(store) {
axios
.get(
"https://www.fastmock.site/mock/ae8e9031947a302fed5f92425995aa19/jd/api/user/register"
)
.then((response) => {
console.log("response", response);
const msg = response.data.desc;
console.log("response.data.desc", msg);
store.commit('testChange', msg);
});
}
},
modules: {
}
})
运行: