赞
踩
本次项目基于vue.js,react.js可以看这链接。
前端框架:vue.js,element-ui/iview,脚手架工具vue-cli3.0+
1.官网下载安装node.js,一路回车默认安装就好。附链接:node.js官网
安装完成后打开cmd/powershell查看
2.安装脚手架工具vue-cli,现在的话使用3.0以上版本的偏多。vue3.0以上有vite支持,有兴趣的可以查看我以前探坑写过的一篇文章vite构建vue3.0项目踩坑
本文采用vue2.6.x版本加最新的vue-cli。
3.全局安装vue-cli(npm与cnpm网上很多,不多讲了)
cnpm install vue-cli -g
安装后输入vue -V 查看是否安装成功 (注意这里是大写的V)
若在此之前已经安装过vue-cli3.0以下版本的,需要先卸载低版本(uninstall),再安装。
cnpm install vue-cli -g
4.我们下一个网易云音乐 NodeJS 版 API
下载到本地后npm i,node app.js运行方便用于我们后面的api封装和调试。
至此,就像是打王者,配置好了铭文,装备方案,快捷消息,约上了野王,法王组队,可以开始进入游戏了。
在我们的学习盘新建一个文件夹study,打开powershell,输入vue create my-vue
其中 my-vue 是我们创建的项目的名字。
创建好之后如下图:
按照提示,输入cd my-vue进入项目,再输入npm run serve就可以启动项目了。成功启动之后会看到如下页面:一般默认为 http://localhost:8080/
至此,项目构建完成了,接着开始我们的开发。
安装element-ui,axios,vue-router,less/sass/scss,vuex(根据实际情况看是否需要用到)。
element-ui: element官方
axios: axios使用说明
vue-router: vue-vouter官方
less: npm install less less-loader --save
less文档
vuex: vuex官方文档
对public下的index.html文件简单修改一下默认样式,根据自己实际需要修改或引入reset.css
cli3.0以后构建的项目需要自己配置vue.config.js文件
内容大致如下
var path = require('path') const ENV = process.env.NODE_ENV // console.log(ENV) function resolve(dir) { return path.join(__dirname, dir) } module.exports = { //关闭eslint规范 lintOnSave: false, devServer: { // port: 8080, // host: 'localhost', // 代理 proxy: { '/api': { target: 'http://localhost:8090', pathRewrite: { '^/api': '/hock' } } } }, chainWebpack: config => { // 参考 https://cli.vuejs.org/zh/guide/webpack.html#%E9%93%BE%E5%BC%8F%E6%93%8D%E4%BD%9C-%E9%AB%98%E7%BA%A7 // git: https://github.com/neutrinojs/webpack-chain // 通用配置区域 // 配置别名 config.resolve.alias .set('@src', resolve('src')) // 代码根目录 .set('@components', resolve('src/components')) // 代码组件目录 .set('@api', resolve('src/api')) // 代码接口层根目录 .set('@style', resolve('src/style')) // 代码通用样式目录 .set('@page', resolve('src/page')) // 业务代码目录 .set('@assets', resolve('src/assets')) // 资源目录 .set('@public', resolve('public')) // 环境配置区域 if (ENV == 'development') { // 开发环境配置 } else if (ENV == 'production') { // 部署环境配置 } } }
在src目录下新建router文件夹和views文件夹,分别创建index.js和login.vue文件。
路由分hash模式和history模式,我喜欢干净点,不带#号的,采用history模式。
在index文件中写入:
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) const routes = [{ path: '', name: 'login', component: (resolve) => require(['../views/login.vue'], resolve) }] const router = new Router({ mode: 'history', routes, }) export default router
在main.js文件引入router(顺便把element也引了吧,反正后面要用)
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
再对我们的App.vue文件修改一下,删除#app下的所有,引入router-view
<template>
<div id="app">
<router-view />
</div>
</template>
登录应该都会写,先来个简单的看效果:
login.vue
<style lang="less"> .login { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } </style> <template> <div class="login"> 这是登录 </div> </template> <script> export default { data() { return { } }, created() {}, mounted() {} } </script>
如图所示
稍微改一改,做个简单的登录
点击登录跳转到我们的主界面,需要在views下新增home.vue文件,并添加对应路由(router/index.js)
this.$router.replace({ name: "home" });
使用element的Container 布局容器结合菜单组件一起使用,完成之后如下:
这里的el-aside和el-menu需要自己绑定一下样式,menu组件都是element的被我删减了,代码如下
<style lang="less" scoped> .home-container { width: 100%; height: 100%; .header { display: flex; align-items: center; justify-content: space-between; .header-right { display: flex; align-items: center; .icon-box { width: 46px; height: 60px; display: flex; align-items: center; justify-content: center; font-size: 24px; } .avatar { width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; img { width: 50px; height: 50px; display: block; border-radius: 25px; cursor: pointer; } } .nickname { height: 60px; line-height: 60px; padding: 0 10px; box-sizing: border-box; } } } .main { background: #f6f8f9; } } </style> <style lang="less"> .home-container .el-container { height: 100vh; } .el-aside { transition: all 0.5s ease; } .el-menu-vertical-demo:not(.el-menu--collapse) { width: 200px; min-height: 400px; } </style> <template> <section class="home-container"> <el-container> <el-aside :width="`${isCollapse ? '66px' : '202px'}`"> <el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse" > <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span slot="title">导航一</span> </template> </el-submenu> </el-menu></el-aside > <el-container> <el-header class="header"> <div class="header-left"> <i v-show="!isCollapse" @click="isCollapse = !isCollapse" class="el-icon-s-fold" style="cursor: pointer" /> <i v-show="isCollapse" @click="isCollapse = !isCollapse" class="el-icon-s-unfold" style="cursor: pointer" /> </div> <div class="header-right"> <div class="icon-box"> <el-badge :value="6" class="item"> <i class="el-icon-message" /> </el-badge> </div> <div class="avatar"> <img class="avatar" src="../assets/avatar_01.png" /> </div> <div class="nickname">五更月</div> <div class="icon-box"> <i class="el-icon-arrow-down" /> </div> </div> </el-header> <el-main class="main"> <router-view></router-view> </el-main> </el-container> </el-container> </section> </template> <script> export default { data() { return { isCollapse: false, }; }, created() {}, mounted() {}, methods: { handleOpen(key, keyPath) { console.log(key, keyPath); }, handleClose(key, keyPath) { console.log(key, keyPath); }, }, }; </script>
可以把aside,header单独拎出来做组件,后面组件化再说吧。
接下来就是我们的菜单跳转了,点击menu跳转对应的界面。
这里涉及到子路由children了。这里我以网易云音乐api为调用举例,页面命名采用music。
在views文件夹下新建music文件夹,新建index.vue,search.vue,playMusic.vue三个文件。
playmusic和search先不写内容,写上两个文案即可,用于验证路由跳转是否成功。
index.vue文件写入
<style lang="less"> .music-container { position: relative; } </style> <template> <div class="music-container"> <router-view></router-view> </div> </template> <script> export default { name: 'music', } </script>
然后配置路由文件index.js,基于home往下配置children
const routes = [{ path: '', name: 'login', component: (resolve) => require(['../views/login.vue'], resolve) },{ path: '/home', name: 'home', component: (resolve) => require(['../views/home.vue'], resolve), children: [ { path: '/home/music', name: 'music', // redirect: '/home/music/findMusic', component: (resolve) => require(['../views/music/index.vue'], resolve), children: [ { path: '/home/music/findMusic', name: 'searchMusic', component: (resolve) => require(['../views/music/search.vue'], resolve), }, { path: '/home/music/playMusic', name: 'playMusic', component: (resolve) => require(['../views/music/playMusic.vue'], resolve), } ] } ] }]
OK,回到我们的home.vue文件,配置菜单开始验证跳转
给el-menu添加router属性,暂时先不考虑三级菜单的情况对menu稍微改造一下:
<el-menu :default-active="activePath" class="el-menu-vertical-demo" router @open="handleOpen" @close="handleClose" :collapse="isCollapse" > <el-submenu index="1" v-for="(item, index) in menuList" :key="index"> <template slot="title"> <i :class="`${item.icon}`"></i> <span>{{ item.title }}</span> </template> <el-menu-item-group> <el-menu-item v-for="items in item.submenu" :key="items.id" :index="`${items.path}`" >{{ items.title }}</el-menu-item > </el-menu-item-group> </el-submenu> </el-menu>
默认展开在mounted添加一段代码
this.activePath = this.$route.path
menuList配置:
menuList: [ { name: "findMusic", title: "网易云", icon: "el-icon-service", id: "0", submenu: [ { name: "findMusic", title: "发现音乐", icon: "", id: "0-1", path: "/home/music/findMusic", }, { name: "playMusic", title: "播放音乐", icon: "", id: "0-2", path: "/home/music/playMusic", }, ], }, ],
好了,完成之后打开可以正常跳转了,效果如下:
到此,就可以按照这个顺序编写页面样式开发了,接下来说一下axios的封装和使用。
在src下新建api文件夹,创建request.js文件
import axios from 'axios' // import store from '@src/store' 暂时不用 // import { HTTP_CODE } from '@lib/const.js' 暂时不用 import Vue from 'vue' import Router from '@src/router' // import { LOGIN_OUT } from '@src/store/mutation-types' 暂时不用 // import auth from '@lib/utils/auth.js' const env = process.env.NODE_ENV // 根据域名判断是否测试环境;替换vue.manageplatform.com为你的线上域名 function IsTest() { let hostUrl = window.location.href return hostUrl.indexOf('vue.manageplatform.com') == -1 } //请求的接口地址--自定义 const HOST = env === 'development' ? 'http://localhost:3000' : IsTest() == true ? 'https://api.mtnhao.com/' : 'https://api.mtnhao.com/' const CancelToken = axios.CancelToken const source = CancelToken.source() const request = axios.create({ baseURL: HOST, timeout: 30000 }) request.defaults.headers.post['Content-Type'] = 'application/json' request.interceptors.request.use(config => { if (config.cancel) { config.cancelToken = source.token source.cancel() } config.url = subSplash(config.url) // token验证 -- 根据自己的实际情况写入,我这里后端需要每次请求默认带上token // if (store.getters.isLogin == true) { // const token = store.state.user.user.token // if (token && config.noToken !== true) { // if (config.method === 'get') { // config.params = { ...config.params, token } // } else if (config.method === 'post') { // if (config.headers['Content-Type'] == 'multipart/form-data') { // config.data.append('token', token) // } else { // config.data = { ...config.data, token } // } // } // } // } // if (config.needSign) { // if (!config.data) { // config.params = auth.Auth(config.params) // } else { // config.data = auth.Auth(config.data) // } // } return config }) request.interceptors.response.use( response => { // 错误统一拦截--根据自己的实际情况来修改 if (parseInt(response.data.status) === 403) { // store.commit(LOGIN_OUT) // vuex登出处理 new Vue().$message.error({ message: '登录已过期,请重新登录', onClose: () => { Router.push({ name: 'login' }) } }) return Promise.reject(response.data.msg) } return response }, error => { return Promise.reject(error) } ) // 删除restapi末尾的反斜杠 function subSplash(url) { return url.endsWith('/') ? url.slice(0, -1) : url } export default request
首先启动我们下好的网易云api,成功后运行在 http://localhost:3000 端口
对应我们views文件夹下的music,在api文件夹下也创建一个music的文件夹,创建search.js文件,写入代码
import request from '../request' // 搜索歌曲 const SearchSongs = ({ pageSize, start, keywords }) => { return request .get('/search', { params: { limit: pageSize, offset: start, keywords, }, }) .then((result) => { return result.data }) } export default { SearchSongs }
对search.vue简单做个搜索功能
<div class="search-bar">
<div class="search-title">{{ keywords }}的搜索结果:</div>
<el-input
class="search-input"
v-model="keywords"
placeholder="请输入"
@change="SearchInfo"
></el-input>
</div>
引入api
import searchApi from "../../api/music/search";
methods: {
SearchInfo() {
searchApi
.SearchSongs({
pageSize: 10,
start: 0,
keywords: this.keywords,
})
.then((res) => {
console.log("res", res);
});
},
},
完成之后结果如下:
好啦,基本流程大致如下,后面再更新vuex,组件封装。
这里有一篇很详细的使用介绍可以参考;可以尝试把搜索出来的结果存到store试手。
vue中使用vuex(超详细)
拿menu举例,
把el-menu剪切出来,到component文件夹下新建home文件夹,再新建menu.vue,
创建好vue模板后往template粘贴。
<style lang="less" scoped> </style> <template> <el-menu :default-active="activePath" class="el-menu-vertical-demo" router @open="handleOpen" @close="handleClose" :collapse="isCollapse" > <el-submenu :index="`${index + 1}`" v-for="(item, index) in menuList" :key="index" > <template slot="title"> <i :class="`${item.icon}`"></i> <span>{{ item.title }}</span> </template> <el-menu-item-group> <el-menu-item v-for="items in item.submenu" :key="items.id" :index="`${items.path}`" >{{ items.title }}</el-menu-item > </el-menu-item-group> </el-submenu> </el-menu> </template> <script> export default { props: { menuList: { type: Array, default: [], }, isCollapse: { type: Boolean, default: false, }, activePath: { type: String, default: "/home/music/findMusic", }, }, data() { return {}; }, methods: { handleOpen(key, keyPath) { console.log(key, keyPath); }, handleClose(key, keyPath) { console.log(key, keyPath); }, }, }; </script>
在home.vue文件使用组件
<el-aside :width="`${isCollapse ? '66px' : '202px'}`">
<home-menu :isCollapse="isCollapse" :menuList="menuList" :activePath="activePath"></home-menu>
</el-aside>
import HomeMenu from "../components/home/menu.vue"
components: {
HomeHeader,
HomeMenu
},
完成之后依然OK
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。