使用Vue CLI创建项目,编写单文件组件

效果图以及分页效果:
效果图1
在这里插入图片描述

一、创建步骤:通过创建vue.cli搭建脚手架,全局安装npm:npm i-g@vue/cli,并且利用IDE模板创建项目lesson8(vue-cli)默认项目。也可以用过cmd中的vue ui进入通过界面来创建
在这里插入图片描述
在这里插入图片描述

二、安装vue.cli项目时,名为vue.cli.service命令默认在preset的package.json运行起来,npm run serve使用热更新在浏览器中打开,vue config配置安装、vue.config.js可选的配置文件,被@vue/cli-service自动加载
结构展示
在这里插入图片描述在这里插入图片描述
三、关键代码:
(1)在App.vue中设置header和footer以及样式设置,在components中创建top-menu.vue和bottom-menu.vue的全局组件,顶部和底部的菜单。使用v-for循环定义模块,传入变量:index==menuIdx样式和事件,在父组件APP.vue中添加function,引入后进行注册,menuIdx默认为0
(2)插槽slot,利用父组件进行扩展。并且使用具名插槽使得左右两边各有内容,再使用作用域插槽,绑定数据属性,子组件绑定数据提供给父组件访问,访问绑定的子组件数据或替代显示。最终显示点赞数
(3)动态组件,使用v-if和v-show实现功能,进行动态加载,通过component动态绑定。
”currentComponent :list=”imglist,利用两个图片组件,使用图片模拟加载,并引入注册ImgList和ImgdetailList。在菜单切换时使用原来的top-menu中的menu-click事件进行捕获,传入参数进行接收方法:如果为0则使用组件ImgList否则使用组件ImgdetailList组件。根据currentComp属性来设置对应组件。
(4)分页效果:导入image-pagelist1和image-pagelist2.vue,注册并且添加标签,在窗口路径设置为老师的源代码E:\Web-3\实验文档\lesson7\jsonServer,在命令行输入mode.index获取端口号3000,访问image-pagelist1,取第一页的8张图片,在image-pagelist1.vue中调用了getCallback,取出渲染后再打印。
以下是代码:
mock-login-login.db.js

let Mock = require('mockjs');
var Random = Mock.Random

function buildData() {
	let data = [{
			code: 0,
			msg: '登录成功!',
			data: Mock.mock({
				'role|1':['ROLE_STUDENT', 'ROLE_TEACHER', 'ROLE_ADMIN'],
				'userInfo': {
					'userId': /5101[0-9]{3}/,
					'userName': '@cname'
				}
			})
		},
		{
			code: 300,
			msg: '登录失败!',
			data: {}
		},
	]
	let i = Random.integer(0, 1)
	return data[i];
}

module.exports = {
	name: '/api/login',
	data: buildData()
};


mock-stdent-studentlist.db.js

let Mock = require('mockjs');
var Random = Mock.Random
function buildData() {
	let data=[];
	for(var i = 1; i <= 15; i++) {
		data.push(
			Mock.mock({
				'id': i,
				'studentId': /5187101[0-9]{3}/,
				'name':'@cname',
				'pic':Random.image('200x200',Random.color()),
				'gentle|1': ['男', '女'],
				'classname': /2018级软件工程[1-5]{1}班/,
				'address': '@province' + '@city' + '@county'
			})
		);
	}
	return data;
}

module.exports = {
	name: '/api/students',
	data: buildData()
};

mock-mockData.js

const path = require('path');
const glob = require('glob'); //匹配规则
var v = path.resolve(__dirname, "./*/*.db.js");
var files = glob.sync(v, {
	nodir: true
});
let data = {};
for (var i = 0; i < files.length; i++) {
	let api = require(files[i]);
	data[api.name] = api.data;
}

module.exports = function(app) {
	app.get("/api/students", function(req, res) {
		res.jsonp(data["/api/students"]);
	});
	app.post("/api/login", function(req, res) {
		res.jsonp(data["/api/login"]);
	})
}

public中保存图片和图标
src-assets保存图片
src-components
bottom-menu.vue:

<template>
	<div class="menu-wrap-active">
			<!-- <div v-for="(item,index) in menus" 
				@click="menuClick(index)"  :key="index"
				:class="menuIdx==index?'item-active1':'item-img'">
				{{item}}
			</div> -->
			<img v-for="(item,index) in menus" @click="menuClick(index)" :key="index" 
			:src="item" :class="menuIdx==index?'item-active1':'item-img'" />
	</div>
</template>

<script>
	export default {
	   name: "bottomMenu",
	   props:['menus'],
	   data:function(){
	 	  return{
	 		  menuIdx:0
	 	  }
	   },
	   methods:{
	 	  menuClick(e){
	 		  this.menuIdx=e;
	 	  }
	   }
	  
	   
	 };
</script>

<style>
	.menu-wrap-active{
		width:100%;
		display:flex;
		flex-direction:row;
		justify-content:space-around;
		margin-top:5px;
		
	}
	
	.item-img {
		height: 28px;
		width: 28px;
		opacity: 0.3;
		
	}
	
	.item-active1 {
		width: 32px;
		height: 32px;
		opacity: 1;
		
	}
</style>

image-list.vue

<template>
	<div class="content">
		<div v-for="(item,index) in list" :key="index" class="item">
			<div>
				<img :src="item.imgurl" />
				<div class="name-text">{{item.name}}</div>
			</div>
		</div>

	</div>
</template>

<script>

	export default {
		props: ['list']
	};
</script>

<style scoped="scoped">
	.content {
		width: 100%;
		height: 100%;
		display: flex;
		flex-flow: row wrap;
		flex: 2;
	}

	.item {
		width: 150px;
		margin-bottom: 20px;
	}

	.name-text {
		margin-top: -20px;
		color:red;
	}
	img{
		width: 150px;
	}
</style>

image-pagelist1.vue

<template>
	<div class="content">
		<div v-for="item in list" :key="item.id" class="item">
			<div>
				<img :src="item.pic" />
				<div class="name-text">{{item.name}}</div>
			</div>
		</div>
	</div>
</template>

<script>
	export default {
		data: function() {
			return {
				list: [],
				pageIndex: 1,
				pageSize: 8,
				firstTouchY: 0,
				isMove:false,
				more: true, //是否还有数据
			}
		},
		methods: {
			getStudents() {
				let getstr = `?_page=${this.pageIndex}&_limit=${this.pageSize}`
				console.log('getstu')
				this.axios.get("/students" + getstr)
					.then((res) => {
						console.log(res.data)
						this.getCallback(res.data);
					})
			},
			getCallback(res) {
				this.list = this.list.concat(res.data);
				console.log(res.len)
				this.isMove=false;
				let pageTotal = res.len / this.pageSize;
				if (res.len % this.pageSize != 0)
					pageTotal = pageTotal + 1;
				if (this.pageIndex >= pageTotal) this.more = false;
			},
			onScroll() {
				let innerHeight = document.querySelector('.content').clientHeight
				let outerHeight = document.documentElement.clientHeight
				let scrollTop = document.documentElement.scrollTop
				console.log(innerHeight,outerHeight,scrollTop)
				if (outerHeight + scrollTop >= innerHeight + 100) {
					console.log(this.more,this.isMove)
					if(this.more&&(!this.isMove)){
						console.log(this.list)
						this.pageIndex++;
						this.isMove=true;
						this.getStudents();
					}
				}
			}
		},
		created: function() {
			this.getStudents();
			window.addEventListener('scroll', this.onScroll)		
		}
	};
</script>

<style scoped="scoped">
	.content {
		width: 100%;
		height: 100%;
		display: flex;
		flex-flow: row wrap;
		flex: 2;
		overflow-x: hidden;
		overflow-y: scroll;
	}

	.item {
		width: 150px;
		margin-bottom: 20px;
	}

	.name-text {
		margin-top: -20px;
		color: red;
	}

	img {
		width: 150px;
	}
</style>

image-pagelist2.vue

<template>
	<div class="content" @touchstart="touchStart" 
		@touchmove="touchMove" @touchend="touchEnd">
		<div v-for="item in list" :key="item.id" class="item">
			<div>
				<img :src="item.pic" />
				<div class="name-text">{{item.name}}</div>
			</div>
		</div>
	</div>
</template>

<script>
	export default {
		data: function() {
			return {
				list: [],
				pageIndex: 1,
				pageSize: 6,
				firstTouchY: 0,
				more: true, //是否还有数据
				isBottom: false,
				isMoved: false,
				changeY:0
			}
		},
		methods: {
			getStudents() {
				let getstr = `?_page=${this.pageIndex}&_limit=${this.pageSize}`
				this.axios.get("/students" + getstr)
					.then(res => {
						this.getCallback(res.data);
					})
			},
			getCallback(res) {
				this.list=this.list.concat(res.data);
				console.log(this.list);
				let pageTotal = res.len / this.pageSize;
				if (res.len % this.pageSize != 0)
					pageTotal = pageTotal + 1;
				if (this.pageIndex >= pageTotal) this.more = false;
			},
			scrollY() {
				return window.pageYOffset ||
					window.document.documentElement.scrollTop
			},
			touchStart(ev) {
				if (!this.more) return
				console.log("scollY="+this.scrollY());
				console.log("document.body.scrollHeight="+document.body.scrollHeight);
				console.log("document.documentElement.clientHeight="+document.documentElement.clientHeight)
				this.firstTouchY = parseInt(ev.changedTouches[0].clientY)
				this.isBottom = this.scrollY() >= (document.body.scrollHeight - document.documentElement.clientHeight-100)
			},
			touchMove(ev) {
				if (!this.isBottom ) return
				this.changeY = parseInt(ev.changedTouches[0].clientY) - this.firstTouchY
				if (this.changeY < 0 && this.more) {
					this.isMoved = true
				}
			},
			touchEnd(ev) {
				if (!this.isBottom || !this.isMoved || !this.more) return				
				this.pageIndex++;
				this.getStudents();
				this.isMoved = false;
			},
		},
		created: function() {
			this.getStudents();
		}
	};
</script>

<style scoped="scoped">
	.content {
		width: 100%;
		height: 100%;
		display: flex;
		flex-flow: row wrap;
		flex: 2;
		overflow-x: hidden;
		overflow-y: scroll;
	}

	.item {
		width: 150px;
		margin-bottom: 20px;
	}

	.name-text {
		margin-top: -20px;
		color: red;
	}

	img {
		width: 150px;
	}
</style>

imagedetail-list.vue

<template>
	<div class="content">
		<div v-for="(item,index) in list" :key="index" class="item">
			<div>
				<img :src="item.imgurl" />
				<div class="name-text">{{item.name}}</div>
			</div>
		</div>

	</div>
</template>

<script>
	export default {
		props: ['list'],
		methods: {
			// 滚动
			scroll() {
				let clientHeight = document.documentElement.clientHeight || document.body.clientHeight
				// 设备/屏幕高度
				let scrollObj = document.querySelector('.content') // 滚动区域
				var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
				let scrollHeight = scrollObj.scrollHeight // 滚动条的总高度
	
	console.log("window.pageYOffset="+window.pageYOffset);
	console.log("document.documentElement.scrollTop="+document.documentElement.scrollTop)
	console.log("document.body.scrollTop="+document.body.scrollTop)
	
				// if (scrollTop + clientHeight === scrollHeight) {
				// 	// div 到头部的距离 + 屏幕高度 = 可滚动的总高度
				// 	// 滚动条到底部的条件
				// 	if (this.hasMore && !this.isLoading) {
				// 		this.isLoading = true
				// 		// 请求后台数据
				// 		let getstr = `?_page=${this.pageIndex}&_limit=${this.pageSize}`
				// 		this.axios.get("/students" + getstr)
				// 			.then(res => {
				// 				// 请求成功调用 getMoreCallback 方法
				// 				this.getMoreCallback(res.data);
				// 			})
				// 	}
				// }
			}
		},
		created: function() {
			window.addEventListener('scroll', this.scroll)
		}
	};
</script>

<style scoped="scoped">
	.content {
		width: 100%;
		height: 100%;
	}

	.item {
		width: 100%;
		margin-bottom: 50px;
	}

	.name-text {
		color: red;
	}

	img {
		max-width: 350px;
		object-fit: contain;
		justify-items: center;
	}
</style>

search-input.vue

<template>
	<div>
		<div class="header-warp">
			<slot name="left">
				<!-- <img src="../../public/imgs/房子.png" class="myicon"/> -->
			</slot>
			<div class="search-box">
				<img src="../../public/imgs/baseline-search-px.png" class="myicon"/>
				<input class="input-wrap" type="search"  placeholder="请输入需要搜索的内容">
			</div>
			<slot name="right" :heartNum="heartNum">
				<div>{{heartNum}}</div>
			</slot>
		</div>
	</div>
</template>

<script>
	export default{
		data:function(){
			return{
				heartNum:5
			}
		}
	}
</script>

<style scoped="scoped">
	.header-warp{
	    width: 100%;
	    display: flex;
	    flex-direction: row;
	    height: 30px;
	    margin-left: 20px;
	    margin-top: 20px;
		
	}
	.search-box{
	    width: 60%;
	    display: flex;
	    flex-direction: row;
	    align-items: center;
	    vertical-align: middle;	
	    background: white;
	    border-radius: 15px;
	}
	.myicon{
		width: 24px;
		height: 24px;
	}
	.input-wrap{
		border:none;
		outline:none;
	}
</style>

student.vue

<template>
	<div>
		<img :src="imgurl" />
		<div class="name-text">{{name}}</div>
	</div>
</template>

<script>
	export default {
		props: ['imgurl','name']
	};
</script>

<style scoped="scoped">
	.name-text{
		margin-top: -80px;
	}
</style>

student-list.vue

<template>
	<div class="content">
		<div v-for="(item,index) in list" :key="index" class="item">
			<student :imgurl="item.pic" :name="item.name"></student>
		</div>
		
	</div>
</template>

<script>
	import student from "./student.vue"
	export default {
		components:{
			student
		},
		props: ['list']
	};
</script>

<style scoped="scoped">
	.content{
		width: 100%;
		height: 100%;
		display: flex;
		flex-flow: row wrap;
		flex: 2;
	}
	.item{
		width: 200px;
		margin-bottom: 80px;
	}
</style>

top-menu.vue

<template>
	<div class="menu-wrap">
		<div v-for="(item,index) in menus"
				@click="menuClick(index)"  :key="index"
				:class="menuIdx==index?'item-active':'item'">
				{{item}}
		</div>
	</div>
</template>

<script>
	export default {
	  name: "topMenu",
	  props:['menus'],
	  data:function(){
		  return{
			  menuIdx:0
		  }
	  },
	  methods:{
		  menuClick(e){
			  this.menuIdx=e;
			  this.$emit("menu-click",e);
		  }
	  }
	};
</script>
<style>
	.menu-wrap{
		width: 100%;
		display:flex;
		flex-direction:row;
		justify-content: space-around;
	}
	.item{
		font-size: 18px;
		line-height: 18px;
		margin-left: 5px;
	    margin-top: 10px;
	}
	.item-active{
		font-size: 24px;
		line-height: 24px;
		margin-left: 15px;
		margin-top: 10px;
		padding-bottom: 2px;
		border-bottom: 1px solid;
	}
	
</style>

vcode-input.vue

<!-- 输入验证码组件 -->
<template>
	<div class="content">
		<div class="vcode-wrap">
			<div class="item">
				<input type="number" maxlength="1" 
				@blur="focusState[0]=false" 
				v-focus="focusState[0]" 
				@input="ChangeHandler(0)" 
				v-model="vcodes0">
			</div>
			<div class="item">
				<input type="number" maxlength="1" @blur="focusState[1]=false" v-focus="focusState[1]" @input="ChangeHandler(1)" v-model="vcodes1">
			</div>
			<div class="item">
				<input type="number" maxlength="1" @blur="focusState[2]=false" v-focus="focusState[2]" @input="ChangeHandler(2)" v-model="vcodes2">
			</div>
			<div class="item">
				<input type="number" maxlength="1" @blur="focusState[3]=false" v-focus="focusState[3]" @input="ChangeHandler(3)" v-model="vcodes3">
			</div>
			<div class="item">
				<input type="number" maxlength="1" @blur="focusState[4]=false" v-focus="focusState[4]" @input="ChangeHandler(4)" v-model="vcodes4">
			</div>
			<div class="item">
				<input type="number" maxlength="1" @blur="focusState[5]=false" v-focus="focusState[5]" @input="ChangeHandler(5)" v-model="vcodes5">
			</div>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				vcodes0: '',
				vcodes1: '',
				vcodes2: '',
				vcodes3: '',
				vcodes4: '',
				vcodes5: '',
				focusState:[false,false,false,false,false,false]
			};
		},
		computed: {
			vcodeStr: function() {
				if ((this.vcode0 != ' ') && (this.vcode0 != '') &&
					(this.vcode1 != ' ') && (this.vcode1 != '') &&
					(this.vcode2 != ' ') && (this.vcode2 != '') &&
					(this.vcode3 != ' ') && (this.vcode3 != '') &&
					(this.vcode4 != ' ') && (this.vcode4 != '') &&
					(this.vcode5 != ' ') && (this.vcode5 != '')) {
					let res = this.vcodes0 + this.vcodes1 +
						this.vcodes2 + this.vcodes3 +
						this.vcodes4 + this.vcodes5;					 
					return res;
				}
			}
		},
		methods: {
			ChangeHandler(e) {
				switch (e) {
					case 0:
						if ((this.vcodes0 != '') && (this.vcode0 != ' ')) {
							for (let i = 0; i < 6; i++) 
								this.focusState[i] = false;
							this.focusState[1] = true;
						}
						break;
					case 1:
						if ((this.vcodes1 != '') && (this.vcode1 != ' ')) {
							for (let i = 0; i < 6; i++) this.focusState[i] = false;
							this.focusState[2] = true;
						}
						break;
					case 2:
						if ((this.vcodes2 != '') && (this.vcode2 != ' ')) {
							for (let i = 0; i < 6; i++) this.focusState[i] = false;
							this.focusState[3] = true;
						}
						break;
					case 3:
						if ((this.vcodes3 != '') && (this.vcode3 != ' ')) {
							for (let i = 0; i < 6; i++) this.focusState[i] = false;
							this.focusState[4] = true;
						}
						break;
					case 4:
						if ((this.vcodes4 != '') && (this.vcode4 != ' ')) {
							for (let i = 0; i < 6; i++) this.focusState[i] = false;
							this.focusState[5] = true;
						}
						break;
					case 5:
						if ((this.vcodes5 != '') && (this.vcode5 != ' ')) {
							for (let i = 0; i < 6; i++) this.focusState[i] = false;
							this.focusState[0] = true;
						}
						break;
				}
				this.$emit('input', this.vcodeStr);
			}
		},
		created:function(){
			this.focusState[0]=true;
		},
		directives: {
			focus: {
				//根据focusState的状态改变是否聚焦focus
				update: function(el, {value}) 
				{ //第二个参数传进来的是个json
					if (value) {
						el.focus()
					}
				}
			}
		}
	}
</script>

<style scoped="scoped">
	.content {
		/* position: relative; */
		width: 100%;
	}

	.vcode-wrap {
		width: 100%;
		display: flex;
		flex-direction: row;
		justify-content: space-around;
	}

	.item {
		width: 40px;
		height: 40px;
		border: #E5E5E5 1px solid;
	}

	input {
		font-size: 24px;
		line-height: 38px;
		text-align: center;
		width: 38px;
 		border:none;
	}
</style>

App.vue

<template>
  <div id="app">
    <div class="header">
		<search-input class="search-wrap">
			<template v-slot:left>
				<img src="../public/imgs/登陆.png" class="myicon"/>
				  <!-- @click="RegHandler"/> -->
			</template>
			<template v-slot:right="pro" >
				<img src="../public/imgs/heart1.png" class="myicon"/>
				<div v-show="pro.heartNum!=0" style="color: yellow;">
				</div>
					{{pro.heartNum}}
			</template>
		</search-input>
		<top-menu :menus="mymenus"  @menu-click="MenuHandler"  class="menu-wrap"></top-menu>
	</div>
	
	<div class="main">
	
		<!-- <div v-show="showReg" class="body-content"> 
			<span>输入手机号码:</span>
			<input type="tel" v-model="mobile"/>
			<div>{{leftTime}}</div>
			<vcode-input v-model="vcodeStr"></vcode-input>
			<hr>
			<div>{{mobile}}</div>
			<div>{{vcodeStr}}</div>
		</div> -->
		
		<div class="body-content">
			<!-- <component :is="currentComp" 
				:list="imglist"></component> -->
			<image-page-list1></image-page-list1>
			<!-- <image-page-list2></image-page-list2> -->
		</div>
	</div>
	<div class="footer">
		<bottom-menu :menus="imgs"></bottom-menu>
	</div>
</div>
</template>
<!--注册引用登记注册-->
<!--父组件参数返回对象属性-->
<script>
	import SearchInput from './components/search-input.vue'
	import TopMenu from "./components/top-menu.vue"
	import BottomMenu from "./components/bottom-menu.vue"
	import StundentList from "./components/student-list.vue"
	//import VcodeInput from "./components/vcode-input.vue"
	import ImgList from "./components/image-list.vue"
	import ImgdetailList from "./components/imgdetail-list.vue"
	import ImagePageList1 from "./components/image-pagelist1.vue"
	import ImagePageList2 from "./components/image-pagelist2.vue"

		export default {
		  name: 'app',
		  components: {
			TopMenu,
			BottomMenu,
			SearchInput,
			// VcodeInput
			StundentList,
			ImgList,
			ImgdetailList,
			ImagePageList1,
			ImagePageList2
			
			
		  },
		  data:function(){
			  return{
					 mymenus:['动态','推荐','热门','精选'],
					 imgs:['./imgs/房子.png',
						'./imgs/购物.png',
						'./imgs/钱包.png',
						'./imgs/相机.png',
						'./imgs/问答.png'],
					list:[ ],
				
					// mobile:'',
					// showReg:false,
					// timer:null,
					// leftTime:60,
					// vcodeStr:''
					currentComp:'ImgList',
					imglist: [{
								imgurl: require('./assets/images/5.jpg'),
								name: '白雪公主'
							},
							{
								imgurl: require('./assets/images/6.jpg'),
								name: '辛德瑞拉'
							},
							{
								imgurl: require('./assets/images/7.jpg'),
								name: '爱丽儿'
							},
							{
								imgurl: require('./assets/images/8.jpg'),
								name: '贝尔'
							}
						]
					
			  }
			 
		  },
		  methods:{
			  // RegHandler(){
				 //  this.showReg=true;
				 //  this.timer=setInterval(()=>{
					//  if(this.leftTime>0)
					// 	this.leftTime--;
					// else{
					// 	 clearInterval(this.timer);
					// 	 this.leftTime=60;
					//  }
				 //  },1000)
			  // }
			  MenuHandler(e){
			  			  this.menuIdx=e;
			  			  if(this.menuIdx==0)
			  				this.currentComp='ImgList'
			  			  else
			  				this.currentComp='ImgdetailList'
			  }
		  },
		  created(){
			  
		 }
	}
</script>
  <!-- // created(){
	 //  // 直接发送请求,因为已经挂载axios
	 //  this.axios.baseURL="http://localhost:8080"
	 //  this.axios.get("/api/students")
		//    .then((res)=>{
		// 	   // console.log(res.data)
		// 	   this.list=res.data;
		// })
	 //  let p={
		//   uesr:'a11',
		//   passwd:'aaaa'
	 //  }
	 //  this.axios.post("/api/login",p)
		// .then((res)=>{
		// 	console.log(res.data)
		// }) -->


<style scoped="scoped">
.header{
	width: 100%;
	height: 90px;
	position: fixed;
	top: 0;
	left: 0;
	background: #708090;

}
.footer{
	width: 100%;
	height: 40px;
	position: fixed;
	bottom: 0;
	left: 0;
	background: #708090;
}
.main{
	/* width: 100%;
	margin-top: 80px;
	background: url(./assets/images/bg.jpg) no-repeat; */
	position:fixed;
	top: 0;
	left: 0;
	width:100%;
	height:100%;
	min-width: 300px;
	z-index:-10;
	zoom: 1;
	background-color: #fff;
	background: url(./assets/images/bg.jpg);
	background-repeat: no-repeat;
	background-size: cover;
	background-position: center 0;
}
.myicon{
	width: 28px;
	height: 24px;
}
.body-content{
	margin-top: 100px;
	
}

</style>

main.js

import Vue from 'vue';
import App from './App.vue';
import axios from "axios";
import VueAxios from "vue-axios";


Vue.config.productionTip = false
Vue.use(VueAxios,axios)
axios.defaults.baseURL="http://localhost:3000"
new Vue({
  render: h => h(App),
}).$mount('#app')


vue.config.js
const path = require('path')
module.exports = {
	publicPath: '/', //部署应用包时的基本 URL
	outputDir: 'dist', //生成的生产环境构建文件的目录
	lintOnSave: true, //eslint-loader 是否在保存的时候检查
	runtimeCompiler: false, //是否使用包含运行时编译器的 Vue 构建版本
	productionSourceMap: false, //生产环境是否生成 sourceMap 文件
	integrity: false, //生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 标签上启用 Subresource Integrity (SRI)
	// webpack相关配置
	chainWebpack: (config) => {
		config.resolve.alias
			.set('vue$', 'vue/dist/vue.esm.js')
			.set('@', path.resolve(__dirname, './src'))
	},
	configureWebpack: (config) => {
		if (process.env.NODE_ENV === 'production') {
			config.mode = 'production' // 生产环境
		} else {
			config.mode = 'development' // 开发环境
		}
	},
	// css相关配置
	// css: {
	// 	extract: true, // 是否分离css(插件ExtractTextPlugin)		
	// 	sourceMap: false, // 是否开启 CSS source maps		
	// 	loaderOptions: {}, // css预设器配置项		
	// 	requireModuleExtension: false // 是否启用 CSS modules for all css / pre-processor files.
	// },
	parallel: require('os').cpus().length > 1, // 是否使用 thread-loader
	pwa: {}, //PWA 插件相关配置
	// webpack-dev-server 相关配置
	devServer: {
		open: true,
		host: 'localhost',
		port: 8080,
		https: false,
		hotOnly: false,
		// http 代理配置
		proxy: {
			'/api': {
				target: 'http://127.0.0.1:8080/api',
				changeOrigin: true,
				pathRewrite: {
					'^/api': ''
				}
			}
		},
		before:require("./mock/mockData")
	},
	  // 第三方插件配置
	  pluginOptions: {
	
	  }
}

四.总结
1.Vue中的axios前端请求数据:axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:(1)从浏览器中创建 XMLHttpRequest(2)从 node.js 发出 http 请求(3)支持 Promise API(4)拦截请求和响应(5)转换请求和响应数据,取消请求(6)自动转换JSON数据(7)客户端支持防止 CSRF/XSRF(8)体量相对较小。Axios改写为Vue的原型属性,在主入口main.js中引用,之后挂载在vue在原型链上:
2.在组件中使用get数据和post请求:根据实验8参考代码,其实 Vue 和 axios 可以在一起配合的事情不只是访问和展示一个 API。也可以和 Serverless Function 通信,向一个有写权限的 API 发送发布/编辑/删除请求等等。
3.组件的分类:
(1)页面级别的组件:页面级别的组件,通常是pages目录下的.vue组件,是组成整个项目的一个大的页面。一般不会有对外的接口。我们通常开发时,主要就是编写这种组件。
(2)业务上可以复用的基础组件:这一类组件通常是在业务中被各个页面复用的组件,这一类组件通常都写到components目录下,然后通过import在各个页面中使用。这一类组件通常是实现某个功能,比如外卖中各个页面都会使用到的评分系统。这个部分就可以实现评分功能,可以写成一个独立的业务组件。
(3)与业务无关的独立组件:这一类组件通常是与业务功能无关的独立组件。这类组件通常是作为基础组件,在各个业务组件或者页面组件中被使用。目前市面上比较流行的ElementUI和iview等中包含的组件都是独立组件。
4.组件的编写(博客园资料):首先一个组件最重要的两个一定是数据和事件。另外,组件开发要考虑可扩展性,在vue中组件扩展通过slot来实现。
(1)数据主要是指:data和prop。其中data主要是用于组件内部的数据展示,通常是一个函数。而prop是接收外部数据,涉及到数据的校验,数据的扩展等,是非常重要的一个API(2)事件:组件的事件(event)不同于在普通元素身上绑定事件。组件事件应该如何触发,是在父组件中触发还是在组件内部元素身上触发。(3)slot:主要用于组件的扩展。同样是组件开发非常重要的API。综上所述:组件开发中有三个非常重要的API,可以戏称为组件开发三要素:prop,event和slot。接下来我们将从组件开发的角度来分别讲述这三个API的使用(4)prop属性定义了组件可以接收哪些可配置的属性。主要是用来接收父组件传递的数据。props接收属性时可以是数组形式,也可以是对象形式。如果不涉及到类型校验或者其他校验可以直接使用数组形式,如果涉及到校验最好使用对象形式。
数组形式:props:[‘name’,’age’]
对象形式: 使用对象的形式,可以对数据的类型,是否必填,以及其他特征进行校验。这对于组件化开发非常有利。Child.vue:
在这里插入图片描述
Parent.vue使用组件

定义组件时,name是String类型且是必填的,age是number类型非必填的。type是必须是success,warning和primary中的某一个。
(5)自定义组件学习:我们定义的Child组件添加点击事件,这时候我们一般是通过在组件内部的button上通过 e m i t 、 触 发 事 件 , 然 后 在 父 组 件 中 监 听 。 在 组 件 中 通 过 emit、触发事件,然后在父组件中监听。在组件中通过 emitemit定义事件:

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值