Django_Vue3_ElementUI_Release_003_前端Vue3项目初始化
机器人迈克猫 2024-10-26 12:33:02 阅读 71
1. 概念扫盲
Node.js是基于ChromeV8引擎,让JS在服务端运行的开发平台,就是JS的一种解释器WebPack就是模块打包机,把浏览器不能直接运行的拓展语言找到并打包为合适的格式给浏览器直接使用Vue基于WebPack构件项目的,并带有合理默认配置的,可以快速开发的完整系统npm就是JS的包管理工具
2. 环境准备
2.1 nodejs下载安装及配置
nodejs download
2.2 安装Vue脚手架
<code>npm install -g @vue/cli
2.3 创建Vue3项目并运行
<code>cd E:\project2024\shopping_car
vue create shopping_car_fore
cd shopping_car_fore
npm run serve
2.4 安装相关包
2.5 vue开发者必备vscode插件【2024最新】
https://blog.csdn.net/liyananweb/article/details/135958361
3. 一个小demo
3.1 public文件夹css,js等文件
3.2 index.html
<code><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">code>
<meta name="viewport" content="width=device-width,initial-scale=1.0">code>
<!-- Favicon and Touch Icons-->
<link rel="shortcut icon" href="xxyy.ico"/>code>
<link rel="stylesheet" href="bootstrap.css">code>
<title>数据平台</title>
</head>
<body>
<div id="app"></div>code>
<!-- Javascript Files -->
<script src="jquery-3.3.1.js"></script>code>
<script src="bootstrap.js"></script>code>
</body>
</html>
3.3 配置main.js
// 导入Vue
import { -- --> createApp } from 'vue'
// 导入Vue扩展插件
import axios from 'axios'
import VueAxios from 'vue-axios'
import { createRouter, createWebHistory } from 'vue-router'
// 导入组件
import App from './App.vue'
// import Product from './components/Product.vue'
import Signin from './components/Signin.vue'
// 定义路由
const routes = [
// 注意,这里定义了sigin为app的component
{ path: '/', component: Signin },
// { path: '/product', component: Product },
]
// 创建路由对象
const router = createRouter({
// 设置历史记录模式
history: createWebHistory(),
// routes: routes的缩写
routes,
})
// 创建Vue对象
const app = createApp(App)
// 将路由对象绑定到Vue对象
app.use(router)
// 将vue-axios与axios关联并绑定到Vue对象
app.use(VueAxios,axios)
// 挂载使用Vue对象
app.mount('#app')
3.4 app.vue
<template>
<div id="app">code>
<router-view/>
</div>
</template>
<script>
export default { -- -->
// 这个vue组件的名字
//export default 语法,这个语法可以让我们将组件定义导出为一个默认的对象。在 Vue3 中,我们可以使用这个语法来定义组件
name: 'App'
}
</script>
3.5 Signin.vue
<template>
<div class="main-layout card-bg-1">code>
<div class="container d-flex flex-column">code>
<div class="row no-gutters text-center align-items-center justify-content-center min-vh-100">code>
<div class="col-12 col-md-6 col-lg-5 col-xl-4">code>
<h1 class="font-weight-bold">用户登录</h1>code>
<p class="text-dark mb-3">民主、文明、和谐、自由、平等</p>code>
<div class="mb-3">code>
<div class="form-group">code>
<label for="username" class="sr-only">账号</label>code>
<input type="text" class="form-control form-control-md" id="username" placeholder="请输入账号"code>
v-model="username">code>
</div>
<div class="form-group">code>
<label for="password" class="sr-only">密码</label>code>
<input type="password" class="form-control form-control-md" id="password"code>
placeholder="请输入密码" v-model="password">code>
</div>
<button class="btn btn-primary btn-lg btn-block text-uppercase font-weight-semibold" type="submit"code>
@click="login()">登录code>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default { -- -->
// vue组件的名字
name: 'Signin',
// 返回的数据,会直接渲染到页面上
data () {
return {
username: '',
password: ''
}
},
methods: {
login: function () {
// 判断是否输入账号
if (this.username.length > 0 && this.password.length > 0) {
// 向后端发送POST请求
let data = new FormData();
data.append('username',this.username);
data.append('password',this.password);
this.axios.post('http://127.0.0.1:8000/', data).then((res)=> {
// POST请求发送成功则获取响应结果的result
// 如果result为true,则说明存在此用户
if (res.data.result) {
// 将访问路由chat,并设置参数
this.$router.push({
path: '/product'
})
} else {
// 当前用户不存在后端的数据库
window.alert('账号不存在或异常')
// 清空用户输入的账号和密码
this.username = ''
this.password = ''
}}).catch(function () {
// PSOT请求发送失败
window.alert('账号获取失败')
// 清空用户输入的账号和密码
this.username = ''
this.password = ''
})
} else {
// 提示没有输入账号或密码
window.alert('请输入账号或密码')
}
}
}
}
</script>
<style scoped>
.text-center {
text-align: center!important;
}
.min-vh-100 {
min-height: 100vh!important;
}
.align-items-center {
align-items: center!important;
}
.justify-content-center {
justify-content: center!important;
}
.no-gutters {
margin-right: 0;
margin-left: 0;
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
*, :after, :before {
box-sizing: border-box;
}
</style>
3.6 如碰到报错
error Component name “index” should always be multi-word vue/multi-word-component-names
解决方案
其一、在项目的根目录找到vue.config.js文件,没有就新创建;
其二、需要添加的代码为:
3.7 运行效果
3.8 总结vue运行原理
从结构上来看,
<code>1. main.js反过来看
2. main.js最终挂载了public文件夹的index.html的#app块,
3. #app块的内容又通过App.vue文件来渲染
4. App.vue又规定了<template>块和<script>块
5. 具体的业务放在components文件夹中,比如Sign.vue就扩充<template>块和<script>块和<script>块就好了。
从内容上来看
1. main.js导入一堆vue的库,负责创建vue实例并挂载到DOM上
体会一下vue3这个例子
<div id="app">code>
<my-component></my-component>
</div>
<script>
const app = createApp({ -- -->});
app.component('my-component', {
template: '<div>A custom component!</div>'
});
app.mount('#app');
</script>
菜鸟教程有更完整的解释
// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = VueRouter.createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: VueRouter.createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
// 5. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
app.mount('#app')
// 现在,应用已经启动了!
4. 正式开始项目
4.1 创建Vue3项目并运行
cd E:\project2024\shopping_car
vue create shopping_car_fore
cd shopping_car_fore
npm run serve
4.2配置vue.config.js
// const { defineConfig } = require('@vue/cli-service')
// module.exports = defineConfig({
// transpileDependencies: true,
// lintOnSave:false
// })
// Vue CLI 3 及以上版本项目中的配置文件,用于配置 Vue.js 应用的各种构建设置
// 这一行,和下面的configureWebpack整个实现别名的配置
const path = require('path')
module.exports = {
// 项目部署的基础路径
// 默认情况下,我们假设你的应用将被部署在域的根目录下
// 比如 https://www.my-app.com/
// 如果应用被部署在一个子路径上,你需要在这里指定子路径
// 比如 https://www.foobar.com/my-app/
// 需要配置为 '/my-app/'
publicPath: '/',
// 将构建好的文件输出到哪里
outputDir: 'dist',
// 放置静态资源的地方 (js/css/img/font/...)
assetsDir: 'static',
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建
productionSourceMap: false,
lintOnSave:false
// webpack-dev-server 相关配置
devServer: {
hot: true,
// 设置端口,前端项目的运行端口
port: 8010,
// 是否在浏览器中自动打开
open: true,
proxy: {
'/': {
// 代理目标地址
target: 'http://127.0.0.1:8000/',
// 是否改变请求源
changeOrigin: true,
// 重写路径
pathRewrite: { '^/': '' },
},
},
},
// 在这里可以进行webpack配置
configureWebpack: {
// 以便在webpack的名称字段中提供应用程序的标题,以便
//可以在index.html中访问它以注入正确的标题。
name: 'system',
resolve: {
alias: {
"~@": __dirname,
"@": path.resolve(__dirname, "./src")
}
}
},
}
更详细的解释参考
https://blog.csdn.net/L_R_H_/article/details/140602086
4.3 public文件夹中放入文件:css,js,img,layui
4.4 src文件夹中创建项目文件夹
axios:配置vue-axios
router:配置和定义Vue的路由信息
store:配置vuex,实现vue状态管理
sytle.css:编写异常页面的样式,仅在异常页面使用,因此不能放在public的css文件夹
4.4 配置Axios和Vuex
4.4.1 安装
<code>npm i axios vue-axios
4.4.2 axios中创建index.js
import axios from 'axios'
axios.defaults.baseURL = '/' //设置HTTP请求地址
axios.defaults.headers.post["Content-Type"] = 'application/json' //设置POST请求的数据类型
axios.defaults.timeout = 60000 //设置HTTP请求超时,单位是毫秒
axios.defaults.withCredentials = true; //默认false,代表跨域请求不提供凭据,改为true表示需要凭据
export default axios
4.5 实现vue数据持久化
vuex能在内存中保存用户数据,实现状态管理,但是页面刷新后数据也会刷新,所以结合vuex-persistedstate实现数据的持久化管理
4.5.1 安装
<code>npm i vuex vuex-persistedstate
4.5.2 store中创建index.js
<code>import { -- -->createStore} from 'vuex' //从Vuex导入函数createStore并实例化生成store对象
import createPersistedState from "vuex-persistedstate";
const store = createStore({
state: { //设置Vuex需要保存的数据
lookImgUrl: 'http://127.0.0.1:8000', //后端api的访问路径,用于设置图片访问地址
// 用于保存用户名和登录时间,数据在个人中心页面展示
username: '',
last_login: ''
},
mutations: { //用于修改参数state的数据,并且只能同步执行
setUserName(state, username){
state.username = username
},
setLastLogin(state, last_login){
state.last_login = last_login
},
},
actions: { }, // 解决参数mutations无法执行的异步问题
modules: { }, // 用于模块化处理,类似于django的模块分发
// 所有数据缓存到本地
plugins: [createPersistedState()], //用于为Vuex引入插件,这里引入vuex-persistedstate实现数据持久化
})
export default store
4.6 编写路由
4.6.1 安装vue-router
npm i vue-router
4.6.2 编写router/index.js
<code>import { -- -->createRouter, createWebHashHistory} from 'vue-router'
// import Home from '../components/Home.vue'
// import Commodity from '../components/Commodity.vue'
// import Detail from '../components/Detail.vue'
// import Shopper from '../components/Shopper.vue'
import Login from '../components/Login.vue'
// import Shopcart from '../components/Shopcart.vue'
// import Error from '../components/Error.vue'
// 定义路由
const routes = [
// {path: '/', component: Home, meta: {title: '首页'}},
// {path: '/commodity', component: Commodity, meta: {title: '商品列表页'}},
// // :id是设置路由变量
// {path: '/commodity/detail/:id', component: Detail, meta: {title: '商品详细页'}},
// {path: '/shopper', component: Shopper, meta: {title: '个人中心页'}},
{ path: '/shopper/login', component: Login, meta: { title: '用户登录页'}},
// {path: '/shopper/shopcart', component: Shopcart, meta: {title: '我的购物车'}},
// // 路由匹配
// {path: '/:pathMatch(.*)*', component: Error, meta: {title: '页面丢失'}},
]
// 创建路由对象
const router = createRouter({
// 设置历史记录模式
history: createWebHashHistory(),
// routes: routes的缩写
routes,
})
export default router
4.7 编写组件
4.7.1 基础组件base.vue
<template>
<div class="header">code>
<div class="headerLayout w1200">code>
<div class="headerCon">code>
<h1 class="mallLogo">code>
<a href="/" title="首页">code>
<img src="img/logo.png">code>
</a>
</h1>
<div class="mallSearch">code>
<div class="layui-form">code>
<input type="text" v-model="search" required lay-verify="required" autocomplete="off"code>
class="layui-input"code>
placeholder="请输入需要的商品">code>
<button class="layui-btn" lay-submit lay-filter="formDemo" @click="mySearch">code>
<i class="layui-icon layui-icon-search"></i>code>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="content content-nav-base" :class="activation">code>
<div class="main-nav">code>
<div class="inner-cont0">code>
<div class="inner-cont1 w1200">code>
<div class="inner-cont2">code>
<router-link :to="`/`" :class="activation == '' ?'active':''">首页</router-link>code>
<router-link :to="`/commodity`" :class="activation == 'commodity' ?'active':''">所有商品code>
</router-link>
<router-link :to="`/shopper/shopcart`" :class="activation == 'shopcart' ?'active':''">购物车code>
</router-link>
<router-link :to="`/shopper`" :class="activation == 'shopper' ?'active':''">个人中心</router-link>code>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default { -- -->
data() {
return {
search: ''
}
},
props: {
activation: {
type: String,
default: ''
},
},
methods: {
// 搜索商品
mySearch: function () {
this.$router.push({ path: '/commodity', query: { search: this.search, page: 1}})
},
}
}
</script>
<style scoped>
</style>
props用于在组件之间传递数据,新建parent.vue和child.vue
parent.vue
<template>
<h3>Parent</h3>
<Child test = "test_string"/>
</template>
<script>
import Child from "./Child.vue"
export default {
components: {
Child
}
}
</script>
child.vue
<template>
<h3>Child</h3>
<p>{ { test }}</p>
</template>
<script>
export default {
props: ["test"]
}
</script>
首先我们在Parent文件的Child标签中以key = value的形式写下我们想要传递给Child的数据,然后在Child的props中接收数据,注意这里要以数组的形式,且key要加双引号。之后,我们就可以在Child文件中正常使用该数据了。
props可以传递多个数据
Parent文件:
<Child test1 = "test_string1" test2 = "test_string2"/>
Child文件:
props: ["test1", "test2"]
其他例子
<div id="app">code>
<blog-post title="My journey with Vue"></blog-post>code>
</div>
<script>
const app = createApp({ -- -->
data() {
return { };
}
});
app.component('blog-post', {
props: ['title'],
template: '<h3>{ { title }}</h3>'
});
app.mount('#app');
</script>
4.7.2 底边栏footer.vue
<template>
<div class="footer">code>
<div class="ng-promise-box">code>
<div class="ng-promise w1200">code>
<p class="text">code>
<a class="icon1" href="javascript:;">7天无理由退换货</a>code>
<a class="icon2" href="javascript:;">满99元全场免邮</a>code>
<a class="icon3" style="margin-right: 0" href="javascript:;">100%品质保证</a>code>
</p>
</div>
</div>
<div class="mod_help w1200">code>
<p>
<a href="javascript:;">关于我们</a>code>
<span>|</span>
<a href="javascript:;">帮助中心</a>code>
<span>|</span>
<a href="javascript:;">售后服务</a>code>
<span>|</span>
<a href="javascript:;">母婴资讯</a>code>
<span>|</span>
<a href="javascript:;">关于货源</a>code>
</p>
</div>
</div>
</template>
<script>
export default { -- -->
name: "Footer"
}
</script>
<style scoped>
</style>
4.8 实例化vue对象main.js
import { createApp } from 'vue'
import VueAxios from 'vue-axios'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from './axios'
import base from './components/Base'
import footer from './components/Footer'
// 创建Vue对象
const app = createApp(App)
// 注册组件
app.component('base-page', base)
app.component('footer-page', footer)
// 将路由对象绑定到Vue对象
app.use(router)
app.use(store)
// 将vue-axios与axios关联并绑定到Vue对象
app.use(VueAxios, axios)
// 挂载使用Vue对象
app.mount('#app')
4.9 根组件app.vue
<template>
<div id="app">code>
<!-- 加载和渲染路由对象router -->
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
4.10 默认首页index.html
<!DOCTYPE html>
<html lang="zh-CN">code>
<head>
<title>母婴商城</title>
<!-- BASE_URL 对应于vue.config.js中配置的publicpath -->
<link rel="stylesheet" type="text/css" href="<%= BASE_URL %>css/main.css">code>
<link rel="icon" href="<%= BASE_URL %>favicon.ico">code>
<link rel="stylesheet" type="text/css" href="<%= BASE_URL %>layui/css/layui.css">code>
<script type="text/javascript" src="<%= BASE_URL %>layui/layui.js"></script>code>
</head>
<body>
<div id="app"></div>code>
</body>
</html>
4.11 业务组件
4.11.1 用户注册和登陆页面,login.vue
<template>
<base-page :activation="activation"></base-page>code>
<div class="login-bg">code>
<div class="login-cont w1200">code>
<div class="form-box">code>
<div class="layui-form">code>
<legend>手机号注册登录</legend>
<div class="layui-form-item">code>
<div class="layui-inline iphone">code>
<div class="layui-input-inline">code>
<i class="layui-icon layui-icon-cellphone iphone-icon"></i>code>
<input name="username" id="username" v-model="username"code>
lay-verify="required|phone" placeholder="请输入手机号"code>
class="layui-input">code>
</div>
</div>
<div class="layui-inline iphone">code>
<div class="layui-input-inline">code>
<i class="layui-icon layui-icon-password iphone-icon"></i>code>
<input id="password" type="password" v-model="password"code>
name="password" lay-verify="required|password"code>
placeholder="请输入密码" class="layui-input">code>
</div>
</div>
</div>
<p>{ -- -->{ msg }}</p>
<div class="layui-form-item login-btn">code>
<div class="layui-input-block">code>
<button class="layui-btn" lay-submit="" @click="loginAndRegister">注册/登录</button>code>
</div>
</div>
</div>
</div>
</div>
</div>
<footer-page></footer-page>
</template>
<script>
export default { -- -->
name: "Login",
data() {
return {
activation: "login",
msg: "",
username: "",
password: ""
}
},
methods: {
loginAndRegister: function () {
console.log(this.username)
console.log(this.password)
this.axios.post('/api/v1/index/login/', { username: this.username, password: this.password}
).then(response => {
this.msg = response.data.msg
if (response.data.state === 'success') {
// 登录成功跳转个人主页
this.$store.commit('setUserName',this.username)
this.$store.commit('setLastLogin',response.data.last_login)
console.log(this.state)
this.$router.push({ path: '/shopper'})
}
})
.catch(function (error) {
console.log(error)
})
}
}
}
</script>
<style scoped>
</style>
4.11.2 用户详情页面,shopper.vue
<template>
<base-page :activation="activation"></base-page>code>
<div class="info-list-box">code>
<div class="info-list">code>
<div class="item-box layui-clear">code>
<div class="item">code>
<div class="img">code>
<img src="img/portrait.png">code>
</div>
<div class="text">code>
<h4>用户:{ -- -->{ username }}</h4>
<p class="data">登录时间:{ -- -->{ last_login }}</p>code>
<div class="left-nav">code>
<div class="title">code>
<router-link :to="`/shopper/shopcart`">我的购物车</router-link>code>
</div>
<div class="title" @click="logout"><a>退出登录</a></div>code>
</div>
</div>
</div>
<div class="item1">code>
<div class="cart">code>
<div class="cart-table-th">code>
<div class="th th-items">code>
<div class="th-inner">code>
订单编号
</div>
</div>
<div class="th th-price">code>
<div class="th-inner">code>
订单价格
</div>
</div>
<div class="th th-amount">code>
<div class="th-inner">code>
购买时间
</div>
</div>
<div class="th th-sum">code>
<div class="th-inner">code>
订单状态
</div>
</div>
</div>
<div class="OrderList">code>
<div class="order-content" id="list-cont">code>
<ul class="item-contents layui-clear" v-for="(o, key) in orders" :key="key">code>
<li class="th th-items">{ -- -->{ o.id }}</li>code>
<li class="th th-price">¥{ -- -->{ o.price }}</li>code>
<li class="th th-amount">{ -- -->{ o.created }}</li>code>
<li class="th th-sum">{ -- -->{ o.state }}</li>code>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="text-align: center;">code>
<div class="layui-box layui-laypage layui-laypage-default" id="layui-laypage-1">code>
<a href="javascript:;" class="layui-laypage-prev">上一页</a>code>
<a href="javascript:;">1</a>code>
<span class="layui-laypage-curr"><em class="layui-laypage-em"></em><em>2</em></span>code>
<a href="javascript:;">3</a>code>
<a href="javascript:;" class="layui-laypage-next">下一页</a>code>
</div>
</div>
</div>
</template>
<script>
export default { -- -->
name: "Shopper",
data() {
return {
activation: 'shopper',
orders: [{ }],
username: this.$store.state.username,
last_login: this.$store.state.last_login,
}
},
mounted: function () {
if (this.$store.state.username === '') {
this.$router.push({ path: '/shopper/login'})
}
this.getcode(); //页面加载时自动执行
},
methods: {
getcode: function () {
var url = '/api/v1/shopper/home/'
var href = window.location.href.split('?')[1]
var t = new URLSearchParams('?' + href).get('t')
if (t !== null){
url += '?t=' + t
}
console.log(url)
this.axios.get(url).then(response => {
this.orders = response.data.data.orders
if (typeof(this.orders) == "undefined") {
this.orders = [{ }]
}
console.log(this.orders)
})
.catch(function (error) {
console.log(error)
})
},
logout: function () {
this.axios.post('/api/v1/shopper/logout/').then(response => { -- -->code>
if (response.data.state === 'success') { -- -->
// 退出登录跳转个人主页
this.$store.commit('setUserName','')
this.$store.commit('setLastLogin','')
console.log(this.state)
this.$router.push({ path: '/'})
}
})
.catch(function (error) {
console.log(error)
})
}
},
}
</script>
<style scoped>
</style>
4.11.3 报错页面,error.vue
<template>
<nav>
<div class="menu">code>
<p class="website_name">母婴商城</p>code>
</div>
</nav>
<div class="wrapper">code>
<div class="container">code>
<div id="scene" class="scene" data-hover-only="false">code>
<div class="circle" data-depth="1.2"></div>code>
<div class="one" data-depth="0.9">code>
<div class="content">code>
<span class="piece"></span>code>
<span class="piece"></span>code>
<span class="piece"></span>code>
</div>
</div>
<div class="two" data-depth="0.60">code>
<div class="content">code>
<span class="piece"></span>code>
<span class="piece"></span>code>
<span class="piece"></span>code>
</div>
</div>
<div class="three" data-depth="0.40">code>
<div class="content">code>
<span class="piece"></span>code>
<span class="piece"></span>code>
<span class="piece"></span>code>
</div>
</div>
<p class="p404" data-depth="0.50">404</p>code>
<p class="p404" data-depth="0.10">404</p>code>
</div>
<div class="text">code>
<article>
<button><router-link :to="`/`">返回首页</router-link></button>code>
</article>
</div>
</div>
</div>
</template>
<script>
export default { -- -->
name: "Error"
}
</script>
<style src="@/assets/style.css" scoped>code>
</style>
4.12 测试页面
4.12.1 测试链接
http://localhost:8010/#/shopper/login
5. 常见报错
5.1 如果后台一直报错
“GET /ws HTTP/1.1”
则修改vue.config.js
5.2 后台接收不到数据
前端是这样的形式
报错要改成绿色方框的内容
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。