参考博客:
--- Install Node.js
--- Node.js 安装配置
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'
}
解决方案参考——nrm报错 ERR_INVALID_ARG_TYPE 解决方法
npm uninstall vue-cli -g
检查并清除 多余的旧版本npm install -g @vue/cli[@版本号]
安装 脚手架脚手架沉淀了许多最佳实践, 可以借助它快速生成Vue工程,包括 项目目录组织、webpack打包配置等;
命令:**vue create [项目名]
**
如**vue create demo-pro
**;
运行创建命令之后,工具会询问创建方式:
这里先选第三个,手动选择创建项目需要的特性,
接着,进入选择特性界面:
用空格进行选择,回车进行确定,
这里选择以上三个特性即可,然后回车:
选择3.x的Vue版本,回车,选择使用ESLint的方式:
这里选择第一个,出错的时候才会触发;
回车确定;
这里是选择Lint的校验时机——保存时校验还是commit时校验,
这里先选择第一个,回车确定;
这里是选择要把**config文件
**,放一个**单独的文件
**里,还是放一个**package.json
**里,
这里先选第一个;
最后问,刚刚这一套特性的选择需不需要保存下来方便后续使用,这里不保存;
回车后工程开始创建:
工程创建完成:
进入工程目录,
使用**npm run serve
**启动工程:
启动中:
启动成功,开始运行:
使用浏览器访问:
cmd处**ctrl + c
**两次可以终止运行:
因为我们无需每次都用**cmd
**去启动项目;
把刚刚创建的项目拉进VSCode,准备使用VSCode启动项目:
在VS Code中,使用Terminal栏启动即可,方便快捷!
【刚拉进来可能启动不了,报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>)
**结构了;**
<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>
运行效果:
首先需要创建一个子组件单文件:
其代码:
<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>
运行效果同上例;
选择特性的时候要选上Router:
这里暂时不选history mode,会使用 hash Router,
适配会简单些:
其他配置:
创建好工程项目后,同样把它拉到VScode里面,
可以看到这里的目录,
可以看到main.js中,多了一个**.use(router)
**
作用:根据url的不同,展示不同的内容;
如下,运行项目,默认url**http://localhost:8080/#/
**,展示主页(Home页):
编写url——**http://localhost:8080/#/about
**访问,则展示about页:
<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
**文件夹下;
-- 可以看到Home.vue这里其实引用一个HelloWorld子组件:
完事,运行,点击Heheda,效果:
如上例程中,**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')
}
]
...
试验,运行上个例程,之后打开浏览器测试工具:
跳到Home页,刷新页面,然后清理记录,再点击about页,
可以看到这个时候页面才加载about的资源:
--- 特性配置:
老规矩,创建项目之后把项目拉进VScode启动与开发;
VueX 其实就是一个**
数据管理框架
**, 它创建了一个全局的、唯一的**数据仓库
**; 当一个前端项目特别大的时候, 或者类似 几十个页面 同步共享 某部分数据 的场景, 我们不可能还是用props、provide、inject等语法去传递数据, 这个时候我们需要一个更加完善的数据管理方案;
main.js中use它:
store /index.js创建仓库,
这里在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>
运行效果:
在About.vue中也试一下:
<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>
运行效果:
流程总结: 要修改数据的组件, 发起**
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: {
}
})
运行:
点击文本,
可见完成了数据的修改,效果:
点击到Home页,
可见这边的数据也跟着改变了,
体现了VueX的**全局特性
**:
即省略**组件的dispatch
**和**store的actions
**的步骤,
组件直接就commit,
然后回调到store的mutations,
直接修改数据:
运行效果基本同上例;
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: {
}
})
运行,点击文本,两秒后文本(即背后的数据)自动改变:
--- 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: {
}
})
运行,点击文本,
两秒后字体改变,效果:
这样设计, --- 可以把同步操作的逻辑封装在**
mutations
**中处理, 把异步操作的逻辑封装在**actions
**中处理; --- 又可以通过对**触发事件名
**的**自定义
**, 对特定的业务处理逻辑、修改数据代码块 做标记; --- 如此使得项目可维护性高、可拓展性高、可读性高, 出问题时容易排查,拓展代码时也比较方便;
--- 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>
运行效果:
<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.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页,
点击文本:
Home页数据同步:
--- 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: {
}
})
找到一个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>
运行效果:
--- 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: {
}
})
运行:
点击文本,两秒后: