前端工程化,專案>>>(vue-cli),建立處vue專案,單頁面應用(spa)
vue-cli建立專案開發,在專案中開發,最後上線,一定要編譯
'純粹的html,js,css'
vue-cli工具由於是nodejs編寫的,所以我們需要node安裝環境1.下載node直譯器
官網下載對應平臺的nodejs直譯器,一路下一步
-安裝後會自動新增環境變數:可執行檔案路徑 就在環境變數
-下載完成後我們開啟cmd,就可以執行node,他的使用和安裝模組的命令,類似python的兩個命令(這兩個命令來自兩個可執行檔案):
-node: 類似cmd中python命令
-npm: 類似cmd中的pip命令
nodejs官網:http://nodejs.cn/
vue版本不同可以使用不同的專案腳手架
2.x 使用vue-cli :https://cli.vuejs.org/zh/
3.x 使用vue-cli
3.x版本的vue專案還可以使用vite建立,vite 效率非常高,但不支援2.x版本的vue專案
1.安裝cnpm工具
npm install -g cnpm --registry=https://registry.npm.taobao.org
2.安裝腳手架(vue-cli)cnpm install -g @vue/cli
-g表示全域性(全域性變數)
安裝完成後,cmd控制檯就可以輸入 vue 命令了(類似裝了djagno可以使用django-admin建立django專案
npm cache clean --force
3.使用vue-cli建立專案
輸入命令:vue create myfirstvue
如果出現上圖情況,y繼續即可
執行命令結束會進入到選擇介面
設定選擇
# vue 專案目錄介紹
myfirstvue # 專案名
node_modules # 非常多第三方模組,以後把專案複製給別人時【上傳git要忽略掉】,這個資料夾刪掉,很多小檔案,專案的依賴,專案要執行,沒有它不行 如果沒有隻需要執行 cnpm install,根據package.json的依賴包,按裝好依賴、
public # 資料夾
-favicon.ico # 網站小圖示(瀏覽器上方的略縮圖)
-index.html # spa 單頁面應用,以後整個vue專案都是用這一個html,但你不用動他
src # 資料夾--以後咱們都動這裡面的
-assets # 靜態資源,以後前端用的圖片,js,css。。都放在這裡
logo.png # 圖片(我們存取的歡迎頁面中的那個圖片就是這個)
-components # 以後在這裡放元件, xx.vue, 小元件
HelloWorld.vue # 提供的展示元件
-router # 安裝了Router,就會有這個資料夾,下面有個index.js
index.js
-store # 安裝了Vuex,就會有這個資料夾,下面有個index.js
-index.js
-views # 頁面元件
-AboutView.vue
-HomeView.vue
-App.vue # 根元件,new Vue範例管理了 div,以後原來寫在div中的東西,現在都寫在App.vue
-main.js # 專案的啟動入口
.gitignore # git的忽略檔案,後面學了git就會了
babel.config.js # bable組態檔,不用動
jsconfig.json # 組態檔,不用動
package.json
# 不用動,安裝了第三方模組,它自動增加
# 內部的資訊中有name屬性,這就是我們專案的名稱,專案啟動後可以在瀏覽器的上方看到
# name屬性的下方,還有一個scripts屬性,內部有一個serve鍵值對,這個serve就是對應:npm run serve中的serve的,如果我們在這裡進行了更改,啟動專案的命令也需要更改
# 下方的dependencies中設定了專案所需模組的版本資訊,版本前面的‘^’號表示的是可以往更高版本相容,但實際使用的時候通常不這麼設定(高版本容易出相容性問題)
package-lock.json # 鎖定檔案,忽略掉
README.md # 使用者手冊
vue.config.js # vue的組態檔
### 看xx.vue 元件學到的重點#########
# 記住,以後開發vue專案,都按照這個模式來
1 新建xx.vue(建立新的元件頁面元件)
2 在xx.vue中就三塊內容
#1 以後元件的html內容,都寫在這裡
<template>
</template>
#2 以後該元件使用的樣式,都寫在這
<style>
</style>
# 3 以後js的東西,都寫在這
<script>
</script>
#### main.js 學到的
# 找到index.html 中的id為app的div,以後都在App.vue中寫(相當於我們之前在html中編寫vue時的el屬性)
new Vue({
render: h => h(App)
}).$mount('#app')
預設匯出語法(使用的多)
-匯出語法
export default 一般是個物件
-匯入語法
import 別名 from '路徑'
以後 別名 就是 匯出的物件
命名匯入匯出語法(瞭解)
命名匯出語法可以寫多個,這樣就可以匯出多個變數
匯出的匯入的時候需要用大括號包裹匯入的變數,並用逗號隔開,需要什麼變數就匯入什麼變數
ps:匯出的時候需要注意,const定義的匯出的變數名不能跟前面程式碼中的變數名重複,會報錯
匯入的簡寫形式
-例如:
# 包是
xiaoming
-index.js
#匯入的時候
import xiaoming from './xiaoming'
# 1 以後只需要寫xx.vue
-頁面元件(views資料夾內建立)
-小元件 給頁面元件用的(components資料夾內建立)
# 2 元件中匯出
'HelloWorld.vue元件匯出後,匯入到HomeView.vue元件中(小元件匯入到頁面元件中)'
'如果元件內部定義了變數需要用data來接收,但是要寫成方法return值'
export default {
name: 'HelloWorld',
data() {
return {
xx: '彭于晏'
}
},
props: {
msg: String
}
}
'如果外面有自定義屬性就需要用props來接收'
<HelloWorld msg="Welcome to Your Vue.js App"/>
# 3 元件如果要在別的元件中使用,需要匯入、註冊
'這裡也是以HelloWorld.vue元件匯入到HomeView.vue元件中為例子'
# 匯入
import HelloWorld from '@/components/HelloWorld.vue'
# 這裡的@符號就是指代src資料夾
import HelloWorld from '../components/HelloWorld.vue'
# 註冊
export default {
components: {
HelloWorld
}
}
# 4 註冊以後,在這個元件中就可以使用匯入的元件 ,寫在<template>
# 自定義屬性
'可以寫但個標籤,也可以寫兩個標籤進行包裹,包裹後內部可以用更多的操作,比如插值語法'
<HelloWorld msg="傳進來的p"/>
props設定項在裡面寫接受父傳子自定義的屬性
# props設定項有三種寫法:
-1 陣列寫法
-2 物件物件寫法
-3 物件套物件寫法
# 寫法總結
# 方式一:使用陣列
props: ['msg'],
# 方式二:使用物件
props: {msg: String}, // 屬性驗證
# 方式三:使用物件,指定型別、預設值和必填
props: {
msg: {
type: String, //型別
required: true, //必要性
default: '老王' //預設值
}
}
# mixin(混入) 可以把多個元件共用的設定提取成一個混入物件
-把多個元件中公用的東西,抽取出來,以後可以全域性使用和區域性使用
# 使用步驟
-1 定義混入物件,新建mixin包,下新建index.js
-2 在 index.js中寫程式碼(元件中會用到的,比如data,methods、設定項)
export const hunhe = {
data() {
return {
name: '彭于晏'
}
},
methods: {
handlePrintName() {
alert(this.name)
}
},
}
-3 區域性匯入:在元件中
import {hunhe} from "@/mixin";
# 設定項:
mixins:[hunhe,]
-4 全域性使用,在main.js 中 以後所有元件都可以用
import {hunhe} from "@/mixin";
Vue.mixin(hunhe)
// Vue.mixin(lqz2) 多個混入需要註冊多次
// Vue.mixin(lqz3)
-5 在元件中,直接使用即可
注意:如上範例中,混入匯入的和methods中的函數都是同一個名字。
直接說結論:先使用methods中的函數,如果在methods中找不到,再用mixins的。
擴充套件:
混入不僅僅可以抽取methods中的函數,還可以抽取data()資料。
查詢順序和之前的一樣,先使用元件自己的資料,如果沒有資料,再去mixins中找資料。
create()這種生命週期勾點函數也可以抽取到mixins。
需要在main.js中匯入mixin包:
全域性引入之後,相當於在所有元件中都加入了mixins設定項,所有元件就都可以使用混入的函數和資料了。
mixins具體使用情景:
比如有一個記錄使用者瀏覽記錄的功能,通過引入mxins,可以實現在每個元件都可以使用這個功能。
# 功能:用於增強Vue
本質:包含install方法的一個物件,install的第一個引數是Vue,第二個以後的引數是外掛使用者傳遞的資料
- elementui相當於是Vue的外掛
# 使用步驟
-新建包plugins,新建index.js
import Vue from "vue";
import axios from "axios";
import {hunhe} from '@/mixin'
export default {
install(miVue) {
// console.log(miVue)
// 1 vue 範例上放個變數
// Vue.prototype.$name = 'lqz' // 類比python,在類上放了一個屬性,所有物件都能取到
// Vue.prototype.$ajax = axios
// 2 使用外掛,加入混入
// 全域性使用混入
// Vue.mixin(hunhe)
// 3 定義全域性元件
// 4 定義自定義指令 v-lqz
Vue.directive("fbind", {
//指令與元素成功繫結時(一上來)
bind(element, binding) {
element.value = binding.value;
},
//指令所在元素被插入頁面時
inserted(element, binding) {
element.focus();
},
//指令所在的模板被重新解析時
update(element, binding) {
element.value = binding.value;
},
});
}
}
2 在main.js 中使用外掛
import plugins from '@/plugins'
Vue.use(plugins) // 本子,使用use,會自動觸發外掛物件中得install
3 以後再元件中可以直接用外掛中寫的東西
# 外掛可以做的事:
1 瞭解,自定義指令(不瞭解沒關係)
2 定義全域性變數,以後在任何元件中都可以使用到,藉助於Vue.prototype往裡放 ,以後所有元件只要this.$ajax 就是axios物件
3 使用全域性混入
4 自定義全域性元件
外掛是包含install方法的一個物件。如上圖我們在index.js匯出這個物件。install方法的第一個引數是Vue範例,第二個以後的引數是外掛使用者傳遞的資料
需求:在Vue範例上存放一個變數,在任意位置通過this.變數名就可以獲取到這個變數的資料。(本質上增強了Vue的功能)
匯入Vue,這裡的Vue是原始碼中的Vue建構函式(把他當作python中的類)
在Vue類上存放了一個屬性$name,以後Vue類產生的物件,都可以拿到這個屬性。
使用外掛,在main.js中匯入外掛
並且使用對外掛使用Vue.use(),相當於自動觸發外掛物件中的install方法。Vue.use()會給自動外掛的第一個引數傳入Vue構造方法(相當於python中的類)。
還可以給外掛傳更多的引數如:Vue.use(plugins, 1, '你好')
在外掛中接收引數
在根元件裡列印this:
這個this是當前的元件物件。檢視控制檯,發現並沒有我們新增的$name屬性。
那到底如何使用外掛,給Vue物件新增屬性呢?使用原型prototype。
修改外掛程式碼:
這裡有個類似python中屬性查詢屬性的問題。我們使用Vue.prototype.$name='lqz'將屬性放入prototype。Vue物件查詢屬性時,會先在自己身上找,如果找不到,就會去prototype找$name屬性。prototype類比python中的類。
使用外掛引入axios:
這樣寫,在任意頁面都可以通過this.$ajax直接使用axios,不用每次使用都匯入了
為什麼屬性的名字前,都需要加一個$?
防止和元件內部的屬性名衝突。
之前我們是在main.js使用Vue.mixin()實現全域性混入。
通過外掛實現全域性混入:
也相當於所有元件都新增了mixin設定項。
elementui外掛定義很多全域性元件,我們可以直接使用:
# 父元件寫了style,子元件都會使用父元件的style
如果在根元件中寫了樣式,會在所有頁面元件都生效,這樣不太好,所有需要使用scoped。
# 父元件的樣式,在子元件中會生效,在style上寫 <style scoped>
# </style>讓該樣式只在當前元件中生效
# 範例
<style scoped>
h1 {
background-color: chartreuse;
}
</style>
# 是window瀏覽器物件(BOM)有的東西
localStorage和sessionStorage和Vue沒有必然聯絡。
# 如果想在瀏覽器中儲存資料:
有三個地方:
1.永久儲存:localStorage 不登入加購物車,沒登入 搜尋過的商品
2.臨時儲存:sessionStorage 關閉頁面資料就沒了
3.設定一個時間,到時候就過期:cookie
# 補充:序列化和反序列化
// 物件轉json字串
// JSON.stringify(person)
// json字串轉物件
// JSON.parse()
# 都是在瀏覽器儲存資料的--》存資料有什麼用?
-登入成功 token存在本地
-不登入加入購物車功能,迪卡儂存在了localStorage中
-元件間通訊----》 跨元件
# localStorage
-永久儲存,除非清空快取,手動刪除,程式碼刪除
-localStorage.setItem('userinfo', JSON.stringify(this.userInfo))
-localStorage.getItem('userinfo')
-localStorage.clear() // 清空全部
-localStorage.removeItem('userinfo')
# sessionStorage
-關閉瀏覽器,自動清理
-sessionStorage.setItem('userinfo', JSON.stringify(this.userInfo))
-sessionStorage.getItem('userinfo')
-sessionStorage.clear() // 清空全部
-sessionStorage.removeItem('userinfo')
# cookie
-有過期時間,到過期時間自動清理
-藉助於第三方 vue-cookies
-cookies.set('userinfo', JSON.stringify(this.userInfo))
-cookies.get('userinfo')
-cookies.delete('userinfo')
# 安裝Vue cookies第三方外掛:
cnpm install vue-cookies -S
# 匯入:
import cookies from 'vue-cookies'
<template>
<div id="app">
<h1>localStorage操作</h1>
<button @click="saveStorage">點我向localStorage放資料</button>
<button @click="getStorage">點我獲取localStorage資料</button>
<button @click="removeStorage">點我刪除localStorage放資料</button>
<h1>sessionStorage操作</h1>
<button @click="saveSessionStorage">點我向localStorage放資料</button>
<button @click="getSessionStorage">點我獲取localStorage資料</button>
<button @click="removeSessionStorage">點我刪除localStorage放資料</button>
<h1>cookie操作</h1>
<button @click="saveCookie">點我向localStorage放資料</button>
<button @click="getCookie">點我獲取localStorage資料</button>
<button @click="removeCookie">點我刪除localStorage放資料</button>
</div>
</template>
<script>
import cookies from 'vue-cookies'
export default {
name: 'App',
data() {
return {}
},
methods: {
saveStorage() {
var person = {
name: '彭于晏',
age: 38
}
localStorage.setItem('userinfo', JSON.stringify(person))
},
getStorage() {
let userinfo = localStorage.getItem('userinfo')
console.log(userinfo)
console.log(typeof userinfo)
},
removeStorage() {
// localStorage.clear()
localStorage.removeItem('userinfo')
},
saveSessionStorage() {
var person = {
name: '彭于晏',
age: 38
}
sessionStorage.setItem('userinfo', JSON.stringify(person))
},
getSessionStorage() {
let userinfo = sessionStorage.getItem('userinfo')
console.log(userinfo)
console.log(typeof userinfo)
},
removeSessionStorage() {
// localStorage.clear()
sessionStorage.removeItem('userinfo')
},
saveCookie() {
cookies.set('name','lqz','7d') // 按秒計
},
getCookie() {
console.log(cookies.get('name'))
},
removeCookie() {
cookies.remove('name')
}
}
}
</script>
<style scoped>
h1 {
background-color: aqua;
}
</style>
# 在vue上,css樣式,用的最多的是elementui,但是還有其他的
-elementui 做網頁端 樣式用的多 vue2的 餓了嗎團隊開發的
-elementui-plus 第三方團隊繼續基於vue3寫的
-vant 做app的樣式 基於Vue3
-iview pc端用www.iviewui.com 基於Vue3
# 使用步驟
1 安裝
cnpm i element-ui -S
2- 在main.js中引入外掛
- 完整引入 (導致專案過大)
- 按需引入 (專業前端)
2.1 設定完整引入 在 main.js 中寫入以下內容
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI) // 以後在咱們元件中直接使用elementui提供的全域性元件即可
3 在元件中使用
-去官網看到好的,複製貼到你的專案中
建立vue專案
使用匯入匯出語法,把 登入功能的傳送請求,抽取到一個包中
# loginView元件頁面
<template>
<div>
<h2 class="top">登入頁面</h2>
<br>
<p>使用者名稱:<input type="text"></p>
<p>密碼:<input type="text"></p>
<p><button>登入</button></p>
</div>
</template>
<script>
export default {
name: 'loginView',
components: {
}
}
</script>
<style>
.top {
align-content: center;
}
</style>
後端部分編寫(django-drf)
因為我們等下會用到django的 authuser表
所以需要我們在 settings檔案中設定
AUTH_USER_MODEL = 'app01.UserInfo'
資料庫部分我們使用預設的即可
INSTALLED_APPS = [
...
'rest_framework' #需要註冊app進行使用Drf
]
在model層建立表
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class UserInfo(AbstractUser):
mobile = models.CharField(max_length=32,null=True)
class CarModel(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
car_factory = models.ForeignKey(to='CarFactory',on_delete=models.CASCADE)
distributors = models.ManyToManyField(to='Distributors')
class CarFactory(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
phone = models.CharField(max_length=32)
class Distributors(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
create superuser
from rest_framework.viewsets import ModelViewSet, ViewSetMixin
from rest_framework.generics import CreateAPIView
from .models import CarModel, UserInfo, CarFactory, Distributor
from .serializer import UserSerializer, CarFactorySerializer, CarModelSerializer, DistributorSerializer
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from django.contrib import auth
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class UserView(ViewSetMixin, CreateAPIView):
# queryset = UserInfo.objects.all()
# serializer_class = UserSerializer
def login(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
user_obj = auth.authenticate(request,username=username,password=password)
if user_obj:
payload = jwt_payload_handler(user_obj)
# 通過payload得到token
token = jwt_encode_handler(payload)
return Response({
'code': 100,
'msg': '登入成功',
'token': token,
'username': user_obj.username,
'url': ''
})
return Response({'msg': '使用者名稱或密碼錯誤', 'code': 101})
class CarModelView(ModelViewSet):
queryset = CarModel.objects.all()
serializer_class = CarModelSerializer
class CarFactoryView(ModelViewSet):
queryset = CarFactory.objects.all()
serializer_class = CarFactorySerializer
class DistributorView(ModelViewSet):
queryset = Distributor.objects.all()
serializer_class = DistributorSerializer
路由層:
from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('car_factory', views.CarFactoryView, 'car_factory')
router.register('car_model', views.CarModelView, 'car_model')
router.register('distributor', views.DistributorView, 'distributor')
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.UserView.as_view({'post':"login"})),
path('user/', include(router.urls)),
]
序列化類:
from rest_framework.serializers import ModelSerializer
from .models import CarModel, UserInfo,CarFactory,Distributor
class UserSerializer(ModelSerializer):
class Meta:
model = UserInfo
fields = "__all__"
class CarModelSerializer(ModelSerializer):
class Meta:
model = CarModel
fields = ['name','price','distributors','car_factory','distributor_list','car_factory_detail','id']
extra_kwargs = {
'distributors': {"write_only": True},
'car_factory': {"write_only": True},
}
class CarFactorySerializer(ModelSerializer):
class Meta:
model = CarFactory
fields = '__all__'
class DistributorSerializer(ModelSerializer):
class Meta:
model = Distributor
fields = '__all__'
<template>
<div>
<h2 class="top">登入頁面</h2>
<br>
<p>使用者名稱:<input type="text" v-model="username"></p>
<p>密碼:<input type="password" v-model="password"></p>
<p>
<button @click="handleLogin">登入</button>
</p>
</div>
</template>
<script>
import axios from "axios"
export default {
name: 'loginView',
components: {},
data(){
return{
username:"",
password:"",
token:''
}
},
methods: {
handleLogin(){
axios.post('http://127.0.0.1:8000/login/',{username:this.username,password:this.password}).then(
res=>{
if (res.data.code === 100){
this.token = res.data.token
}else {
alert(res.data.msg)
}
}
)
}
}
}
</script>
<style>
.top {
align-content: center;
}
</style>
前端程式碼修改
效果:
我們可以將登入功能抽取出來,放到src下面的資料夾中
import axios from "axios"// 這裡必須匯入axios,因為別的包會直接呼叫下面的函數,而下面的函數需要使用這個axios
// 預設匯出
export default {
handleLogin() {
axios.post('http://127.0.0.1:8000/login/', {username: this.username, password: this.password}).then(
res => {
if (res.data.code === 100) {
console.log(res.data)
this.token = res.data.token
} else {
alert(res.data.msg)
}
}
)
}
}
export const ajaxfunc = function handleLogin () { // 這裡如果使用匿名函數,由於匿名函數沒有this,在別的元件中使用會導致獲取不了資料。
axios.post('http://127.0.0.1:8000/login/', {username: this.username, password: this.password}).then(
res => {
if (res.data.code === 100) {
console.log(res.data)
this.token = res.data.token
} else {
alert(res.data.msg)
}
}
)
}
<template>
<img :src="imgaddr">
3. 在圖片頁元件,加一個button,點選把圖片地址顯示在父元件中 (子傳父)
<template>
<div class="about">
<h1>父傳子圖片</h1>
<br>
<button @click="handleSend">點選按鈕將圖片地址傳送到子元件上</button>
<br>
<hr>
<imgShow ref="myimg" @myevent="eventFunc"></imgShow>
<hr>
<h1>子傳父圖片</h1>
<img :src="text">