npm(nodejs package manager)、webpack、Vue组件、Vue脚手架开发工具、Vue Router的使用、Vuex的使用、使用Django前后端交互
13.8 npm(nodejs package manager)
使用命令行安装包: 1. cd切换到项目目录下,执行初始化操作 npm init/npm init -y 2. 安装其他依赖包 npm install jquery npm install jquery@1.11.13 npm install jquery -g 全局安装 npm install bootstrap@3 -D 开发环境下 marked包安装和使用 npm install marked -D npm install vuex -D 3. 卸载包 npm uninstall 包名 4. 更新npm npm install npm@latest 5. npm安装包慢的解决办法: 1.安装cnpm包 :https://npm.taobao.org/ npm install -g cnpm --registry=https://registry.npm.taobao.org 2. 配置npm源为阿里源 npm config set registry https://registry.npm.taobao.org/
在当前项目下生成文件:node_moduels(包含用npm导入的jQuery包等)、package.json、pack-lock.json(包含导入包的信息)
index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script src="./node_modules/jquery/dist/jquery.min.js"></script> <script> $.each([11,22,33], function(k,v){ console.log(k,v) }) </script> </body> </html>
13.9 webpack
为什么要有webpack?
1. JS中不存在模块化的概念
2. 安装和使用
npm install webpack -g --> 全局安装
npm install webpack-cli -g
3. webpack进阶:https://webpack.js.org/
在当前项目下生成文件:dist(包含main.js,将项目下的依赖关系文件打包保存在main.js文件中)、node_moduels(包含用npm导入的jQuery包等)、package.json、pack-lock.json(包含导入包的信息)
x.js:
var alex = 'sb';
var login = true;
module.exports = {alex}
y.js:
var obj = require('./x')
var jquery = require('jquery')
console.log(obj); //sb
jquery.each([11,22,33,44], function(k,v){
console.log(k,v)
})
main.html:
<body> <script src="./dist/main.js"></script> </body>
13.10Vue组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div id="app"> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <component-a></component-a> <ComponentB //父组件向子组件通信 v-for="a in aList" v-bind:url="a.url" v-bind:title="a.title" ></ComponentB> <p> 被选了{{num}}次!</p> <!--子组件向父组件通信--> <ComponentC v-for="name in nameList" v-bind:name="name" v-on:do="foo" ></ComponentC> <table> //组件的is属性 <!-- <ComponentB></ComponentB> 此时组件中template中的tr会显示在table外--> <tr is="ComponentB"></tr> //此时组件中template中的tr会显示在table中的tbody中 </table> </div> <script src="./node_modules/vue/dist/vue.js"></script> <!--通过npm导入vue.js--> <script> //全局注册组件:定义一个名为button-counter的新组件 Vue.component('button-counter', { /* data: { count: 0 } */ data: function (){ //data 必须是一个函数,不能直接是对象,组件复用时会影响到其他实例 return { count: 0 } }, template: '<button v-on:click="count++">你点了我 {{ count }} 次。</button>' }) //局部注册组件:局部注册的组件在其子组件中不可用,如果你希望ComponentB在ComponentA中可用,需声明 const ComponentA = { components: { ComponentB }, template: `<a href='https://www.sogo.com'>点我</a>`, data: function(){ return { } } } //父组件向子组件通信 const ComponentB = { //template: `<a> <slot></slot></a>`,通过插槽slot分发内容 //template: ` <tr><slot></slot></tr>`,组件的is属性 template: `<p><a v-bind:href='url'>{{title}}</a></p>`, props: { //在子组件中使用props声明将url、title传入组件template并显示 url: String, //对传值进行校验 title: { type: String, required: true } }, data: function(){ return { } } } //子组件向父组件通信 const ComponentC = { //子组件可以通过调用内建的 $emit 方法 并传入事件名称来触发一个事件 template: `<button v-on:click='$emit("do")'>{{ name }}</button>`, props: { //使用props声明将name传入组件template并显示 name }, methods: { do(){ } } } var vm = new Vue({ el: '#app', components:{ //局部注册组件需要在components中声明 'component-a': ComponentA, ComponentB, ComponentC }, data: { num:0, nameList: ["技师A", '技师B', '技师C'], aList: [ { url: 'https://www.sogo.com', title: 'sogo' }, { url: 'https://www.luffycity.com', title: 'luffycity' }, { url: 'http://www.oldboyedu.com/', title: 'oldboy edu' }, ] }, methods: { foo(){ this.num += 1; } } }) </script> </body> </html>
13.11Vue脚手架开发工具
1. 安装 npm install -g vue-cli 2. 使用 查看安装的vue-cli版本:vue -V 查看帮助:vue --help 查看支持的模板:vue list 3.创建Vue项目 webpack简单模板:vue init webpack-simple app01 webpack模板:(使用Bootstrap时候要用这个):vue init webpack vueapp01 ? Project name vueapp01 '回车确认' ? Project description A Vue.js project '回车确认' ? Author Lmy <1592187014@qq.com> '回车确认' ? Vue build (Use arrow keys) ? Vue build (standalone) > Runtime + Compiler: recommended for most users '回车确认' Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere ? Install vue-router? 'Yes' ? Use ESLint to lint your code? 'No' ? Set up unit tests No '(设置单元测试)' ? Setup e2e tests with Nightwatch? 'No' '(用夜视器设置e2e测试?)' ? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys) '(npm)' > Yes, use NPM '回车确认' Yes, use Yarn No, I will handle that myself 。。。。( vue-cli · Generated "vueapp01".)。。。。(Installing project dependencies ...)。。。。 Project initialization finished!。。。。。。 '''To get started: cd vueapp01 npm run dev''' C:\untitled>cd vueapp01 C:\untitled\vueapp01>npm run dev '(启动前端服务)'' Your application is running here: http://localhost:8080' 停止项目:Ctrl + C 4.在当前项目下安装bootstrap C:\untitled\vueapp01>npm install bootstrap@3.3.7 -D 或者 npm install bootstrap@3.3.7 --save-d '安装开发环境下的bootstrap,并将依赖关系写入package.json中'
13.12Vue Router的使用
两个组件(Vue Router内置组件):
<router-link to="/foo">Go to Foo</router-link> #默认渲染成a标签 <router-view></router-view> #路由视图,组件显示位置
制作组件路由:
components/Home.vue
<template> <div> <h1>这是home页面</h1> //vue文件中,组件template一定要用div包裹所有标签 </div> </template> <script> export default { name:'Home', } </script> <style> </style>
components/Note.vue
<template> <div> <h1>这是note页面</h1> </div> </template> <script> export default { name:'Note', } </script> <style> </style>
router/index.js
import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Home from '@/components/Home.vue' //从组件导入,@代表src import Note from '@/components/Note.vue' Vue.use(Router) export default new Router({ mode:'history', //去掉URL中的'#' routes: [ //设置组件路由对应关系 { path: '/home', name: '我的home页面', component: Home }, { path: '/note', name: '我的note页面', component: Note } ] })
Apple.vue:
<li><router-link to="/home">link home版</router-link></li> <li><router-link to="/note">link note版</router-link></li> <router-view></router-view>
或
<router-link to="/home" tag="li" #指定生成li标签 active-class="active" #指定标签被点击时的样式 > <a href="">link home版</a> </router-link> <router-link to="/note" tag="li" active-class="active" > <a href="">link note版</a> </router-link> <router-view></router-view>
或
<router-link v-for="(item,index) in allRouters" v-bind:to="item.path" tag="li" active-class="active" v-bind:key=index > <a href="">{{ item.name }}</a> </router-link> <router-view></router-view> <script> import 'bootstrap/dist/css/bootstrap.min.css' export default { name: 'App', // 计算属性 computed:{ allRouters(){ // 当前Vue实例注册的所有路由 return this.$router.options.routes } } } </script>
main.js:
import Vue from 'vue' import App from './App' import router from './router' //导入路由对象 Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, //注册路由对象 components: { App }, template: '<App/>' })
13.13Vuex的使用
store.js:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // 开一家商店 export default new Vuex.Store({ state: { count:0 }, mutations:{ //提交 mutation来更改 Vuex 的 store 中的状态 increment(state){ state.count+=1 } } })
main.js:
import Vue from 'vue' import App from './App' import router from './router' import store from './store' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, //注册路由对象 store, //向vue实例注册我的商店 components: { App }, template: '<App/>' })
NoteItem.vue:
<template> <div class="list-group-item" v-on:click="foo" > {{n}} </div> </template> <script> export default{ name:'NoteItem', props:{ n:String }, methods:{ //v-on监听事件使用method foo(){ //子组件被点击时向父组件传值 ,使用$emit()传递事件,告知父组件子组件被点击 // this.$emit('plus') //使用子组件向父组件传值时使用$emit,使用vuex时注释此项 //this.$store.state.count+=1 //使用vuex,在被点击时修改store中的count this.$store.commit('increment') //使用vuex,在被点击时提交事件increme } } } </script> <style> </style>
NoteList.vue:
<template> <div> <div class="list-group"> <!-- #父组件向子组件传值,将循环数据使用v-bind传给子组件,子使用props接收并使用,后替换NoteItem --> <NoteItem v-for="(note,index) in noteList" v-bind:n='note' v-bind:key='index' v-on:plus='p'> <!-- #子组件向父组件传值,使用v-on监听$emit传递的事件 --> </NoteItem> <p>计数器:{{count}}</p> </div> </div> </template> <script> import NoteItem from '@/components/NoteItem' export default { name:'NoteList', components:{ NoteItem }, data:function() { return { noteList: [ '第一项','第二项','第三项' ], //count:0 //使用子组件向父组件传值时使用data,使用vuex时注释此项 } }, methods:{ //v-on监听事件使用method p(){ //console.log(this.count); this.count+=1 } }, computed:{ //使用vuex时使用此项,要用return返回 count:function() { return this.$store.state.count } } } </script> <style> </style>
Vue组件之间的通信:
父组件->子组件:子组件中要使用:props声明我接收的变量
子组件 -> 父组件:1.子组件 通过this.$emit('事件名') 向父组件抛出事件
2.父组件 通过v-on:事件名='方法名' 监听子组件的事件从而触发一个修改数据的方法
13.14使用Django前后端交互
1.django做后端,(先导入pip3 install django-cors-headers)在Django的settings文件中配置:
#允许跨域请求的IP(因为vue默认8080端口,Django默认8000端口) #授权白名单 CORS_ORIGIN_WHITELIST=( 'http://localhost:8080', 'http://127.0.0.1:8080' )
views.py:
from app01 import models from django.http import JsonResponse def note_list(request): ret = {"code": 0} data = list(models.Note.objects.all().values("id", "title", "content", "markedcontent")) ret["data"] = data return JsonResponse(ret) #返回json字符串
2.vue作前端,使用axios发送请求并接受后端的数据(安装:npm install axios)
App.vue:
<script> import 'bootstrap/dist/css/bootstrap.min.css' export default { name: 'App', data: function () { return {} }, // 计算属性 computed: { // 当前Vue实例注册的所有路由 allRouters() { return this.$router.options.routes } }, beforeMount(){ //在挂载前执行store.js中的playNote函数接受后端数据 this.$store.commit('playNote') }, } </script>
store.js:将接收到的数据放进商店
import Vue from 'vue' import Vuex from 'vuex' import axios from 'axios' //导入axios Vue.use(Vuex) export default new Vuex.Store({ state: { count:0, notelist:[] }, mutations:{ addnote_store(state,data){ //捕获NoteEdit传到store的数据用data接收 state.notelist.push(data) }, playNote(state,data){ //在挂载DOM之前向后端获取数据 axios.get('http://127.0.0.1:8000/api/notes/') //访问note_list视图函数的路由 .then(function (response) { //response接收包含json字符串(ret)在内的数据 //console.log(response.data.data); state.notelist=response.data.data //后端返回到response中的数据(ret)以data命名 }) .catch(function (error) { //获取后端数据失败打印错误信息 console.log(error); }); } }, actions:{ //方法一:直接将新添加的数据使用 addnote_store添加到 notelist:[] addnode_post(context,data){ //异步操作store // 发送 POST 请求 var data=qs.stringify(data) //发送json类型 axios({ method: 'post', url: 'http://127.0.0.1:8000/api/add/', data: data, }) .then(function (response) { console.log('插入数据'); console.log(response); context.commit('addnote_store',response.data.data) // {id: 3, title: "第三条笔记", content: "", markdownContent: ""} //此处只让后端在数据库中添加数据,不添加到notelist:[] }) .catch(function (error) { console.log(error); }) }, } })
NoteList.vue:取出商店里的数据,交给template进行渲染
<template> <div class="list-group"> <NoteItem v-for="(note,index) in noteList" v-bind:n='note' v-bind:key='index' </NoteItem> </div> </template> <script> import NoteItem from '@/components/NoteItem' export default { name:'NoteList', components:{ NoteItem }, data:function() { } }, computed:{ //使用vuex时使用此项 count:function() { return this.$store.state.count }, noteList:function(){ //使用vuex获得store的数据 return this.$store.state.notelist } } } </script>
NoteItem.vue:
<template> <div class="list-group-item" v-on:click="foo" > {{n.title}}{{n.content}} </div> </template> <script> export default{ name:'NoteItem', props:{ //props指定接收父组件传递的数据 n:Object }, methods:{ //v-on监听事件使用method foo(){ //子组件被点击时向父组件传值 ,使用$emit()传递事件,告知父组件子组件被点击 // this.$emit('plus') //使用子组件向父组件传值时使用$emit,使用vuex时注释此项 //this.$store.state.count+=1 //使用vuex,在被点击时修改store中的count this.$store.commit('increment') //使用vuex,在被点击时提交事件increme } } } </script>