2-3 css部分
- 两栏布局
- 三栏布局
- 双飞翼和圣杯布局:和三栏布局要求相同,不过中间列要写在前面保证优先渲染。
- 三角形
- 使用 css 实现一个宽高自适应的正方形
- 实现扇形(border+旋转)
- 垂直居中(left:0,margin:0,top:0,right:0,margin:auto)
- 清除浮动(伪元素clear:both),父级触发BFC
- 弹出框
- 导航栏(滑动)
CSS 部分完,总结,Flex 无敌。
2-4 js 部分
- hash路由
window.addEventlistener('hashchange',(e)=>{
location.hash
} )
1.监听
2.操作BOM
- history路由
调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。
2-4 css部分
- 扩大可点击区域
.expand-range {
position: relative;
cursor: pointer;
}
.expand-range::after {
content: '';
position: absolute;
top: -10px;
right: -10px;
bottom: -10px;
left: -10px;
}
scss方式
@mixin expand-range($top: -10px, $right: $top, $bottom: $top, $left: $right, $position: relative,$cursor:pointer) {
position: $position;
cursor: $cursor;
&:after {
content: '';
position: absolute;
top: $top;
right: $right;
bottom: $bottom;
left: $left;
}
}
//使用:.test { @include expand-range($top: -5px) }
-
巧用层叠上下文
元素的层叠关系,背景,负z-index,盒子z-index
利用伪元素实现圆角矩形并叠加在父元素的背景之上文字之下
/*边框内圆角*/
div {
position: relative;
z-index: 1;
height: 200px;
padding: 10px;
background: #333;
}
div::after {
content: '';
position: absolute;
left: 10px;
top: 10px;
right: 10px;
bottom: 10px;
z-index: -1;
border-radius: 5px;
background: cyan;
}
- clip-path
- min-content (自适应宽度)
- box-shadow
- 解析background
- linear-gradient
- animation
总结:背景颜色,渐变,动画,position,box-shadow
2-4 vue部分
- 说说你对 SPA 单页面的理解,它的优缺点分别是什么?
- v-show 与 v-if 有什么区别?
- Class 与 Style 如何动态绑定?(都是对象和数组,稍微有点区别而已,style数组的值是写好的在data上)
- 怎样理解 Vue 的单向数据流?(props)
- computed 和 watch 的区别和运用的场景?
- 直接给一个数组项赋值,Vue 能检测到变化吗?
- 谈谈你对 Vue 生命周期的理解?
- Vue 的父组件和子组件生命周期钩子函数执行顺序?
- 在哪个生命周期内调用异步请求?
- 父组件可以监听到子组件的生命周期吗?
- 谈谈你对 keep-alive 的了解?
<keep-alive>
<router-view />
</keep-alive>
直接嵌套二级组件是不会缓存的
那么就会缓存在Ndode节点中了,就不会重新创建,那么也就不会触发生命周期了 activated,deactivated
组件内路由导航:beforeRouteEnter,beforeRouteLeave
如果想记住二级切换的是哪个组件
data(){
return {
path:/home
}
}
activated(){
this.$router.push(this.path)
},
deactivated(){},
beforeRouteLeave(to,from,next){
this.path =this.$route.path
next()
}
这样就是记住上一个二级路由
- 组件中 data 为什么是一个函数?
- v-model 的原理?
在表单 input、textarea、select 等元素上创建双向数据绑定
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:
<ModelChild v-model="message"></ModelChild>
子组件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
- Vue 组件间通信有哪几种方式?
- 能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?
- 什么是 MVVM?
- Vue 是如何实现数据双向绑定的?
- Vue 框架怎么实现对象和数组的监听?
- Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题
2-6 ES6部分
- ES6面向对象
通过class关键字创建类,类名我们还是习惯性定义首写字母大写
类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
constructor 函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
生成实例 new 不能省略
最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面要加小括号,构造函数不需要加function
我们类里面的所有函数不需要写function
多个函数方法之间不需要添加逗号分隔
class Name {
constructor(x,y){
this.x = x;
this.y = y;
}
say(){}
good(){}
}
// 继承
class Father{
constructor(x,y){
this.x = x;
this.y = y;
}
say(){
console.log('father')
}
}
class Son extends Father{
constructor(x,y){
super(x,y)
}
}
// 私有属性方法
const name = Symbol("name");
const print = Symbol("print");
class Person{
constructor(age){
this[name] = 'aking'
this.age = age;
}
[print](){
console.log(`${this[name]} is ${this.age} years old`)
}
}
// 混合继承
function CopyProperties(target, source) {
for (const key of Reflect.ownKeys(source)) {
if (key !== "constructor" && key !== "prototype" && key !== "name") {
const desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
function MixClass(...mixins) {
class Mix {
constructor() {
for (const mixin of mixins) {
CopyProperties(this, new mixin());
}
}
}
for (const mixin of mixins) {
CopyProperties(Mix, mixin);
CopyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
class Student extends MixClass(Person, Kid) {}
2-7 vue通信的细节
- props
- this.$xxx
通过this下面的数据直接获取vue
实例这种方法比较暴力,因为我们所谓的组件,最终都会是一个对象,存放组件的各种信息,组件和组件通过this.$children
和this.$parent
指针关联起来。因为在项目中只有一个root
根组件,理论上,我们可以找到通过this.$children
this.$parent
来访问页面上的任何一个组件 ,但是实际上如何精确匹配到目标组件,确是一个无比棘手的问题。
- provide inject
// 父组件
provide(){
return {
/* 将自己暴露给子孙组件 ,这里声明的名称要于子组件引进的名称保持一致 */
father:this
}
},
// 这里我们通过provide把本身暴露出去。⚠️⚠️⚠️这里声明的名称要与子组件引进的名称保持一致
// 子组件
/* 引入父组件 */
inject:['father'],
methods:{
send(){
this.father.sonSay(this.mes)
}
},
// 父组件
// 如果我们向外提供了方法,如果方法里面有操作this行为,需要绑定this
provide(){
return {
/* 将通信方法暴露给子孙组件(注意绑定this) */
grandSonSay:this.grandSonSay.bind(this),
sonSay:this.sonSay.bind(this)
}
},
methods:{
/* 接受孙组件信息 */
grandSonSay(value){
this.grandSonMes = value
},
/* 接受子组件信息 */
sonSay(value){
this.sonMes = value
},
},
// 子组件
/* 引入父组件方法 */
inject:['sonSay'],
methods:{
send(){
this.sonSay(this.mes)
}
},
- vuex
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
fatherMes: 'fatherMes',
sonMes: 'sonMes',
fatherMesAsync: 'fatherMesAsync'
},
mutations: {
sayFaher(state, value) {
state.fatherMes = value
},
saySon(state, value) {
state.sonMes = value
},
sayAsyncFather(state, value) {
state.fatherMesAsync = value
}
},
actions: {
asyncSayFather({ commit }, payload) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(payload)
console.log(payload)
}, 2000)
}).then(res => {
console.log('后执行')
commit('sayAsyncFather', res)
})
}
}
})
<template>
<div class="father">
<input v-model="mes" /> <button @click="send">同步:对子组件说</button
><br />
<input v-model="asyncMes" />
<button @click="asyncSend">异步:对子组件说</button><br />
<div>子组件对我说:{{ sonMes }}</div>
</div>
</template>
<script>
export default {
/* 父组件 */
name: 'father',
data() {
return {
mes: '',
asyncMes: ''
}
},
computed: {
sonMes() {
return this.$store.state.sonMes
}
},
mounted() {
console.log(this.$store)
},
methods: {
/* 触发mutations,传递数据给子组件 */
send() {
this.$store.commit('sayFaher', this.mes)
},
/* 触发actions,传递数据给子组件 */
asyncSend() {
this.$store.dispatch('asyncSayFather', this.asyncMes)
}
}
}
</script>
- 总结:通信就是组件实例之间的数据相互传递,组件实例相互暴露。
vue最终版
- v-bind="props"v−bind="attrs" v-on="$listeners"
post: {
id: 1,
title: 'My Journey with Vue',
placeholder:'默认'
}
<!-- 利用 v-bind 可以传入一个对象的所有 property,类似 v-bind="Obj" -->
<blog-post v-bind="post"></blog-post>
<!--等价于-->
<blog-post
v-bind:id="post.id"
v-bind:
></blog-post>
<input type="text" v-bind="$attrs"/>
// top组件,传递了name,age,gender,sdf四个属性到子组件center,然后接收了两个isClick()和asd()方法
<template>
<section>
<centers
name="name"
age="18"
gender="666"
sdf="asd"
@isClick="isClick"
@asd="asd"
></centers>
</section>
</template>
<script>
import centers from '~/components/center';
export default {
components: {
centers
},
methods: {
asd() {
console.log(999);
},
isClick() {
console.log(666);
}
}
};
</script>
// 未识别的事件可通过v-on="$listeners"传入(.native绑原生事件是没用的) center组件
<template>
<section>
<div class="mt-10">
<bottom v-bind="$attrs" v-on="$listeners" />
</div>
</section>
</template>
<script>
import bottom from '~/components/bottom';
export default {
components: {
bottom
},
props: {
name: {
type: String,
default: 'default'
},
age: {
type: String,
default: 'default'
}
}
};
</script>
//bottom组件
<template>
<section>
<div>
{{ $attrs['gender'] }} 在$attrs里面只会有props没有注册的属性
<br>
{{ gender }}
</div>
</section>
</template>
<script>
export default {
props: {
gender: {
type: String,
default: ''
}
},
mounted() {
console.log(this.$attrs);
console.log(this.$listeners);
this.$listeners.isClick();
this.$listeners.asd();
}
};
</script>
- Props 校验
Vue.component('my-component', {
// 带有默认值的对象
propA: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
}
})
Vue.component('my-component', {
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
})
- 插槽
<!-- 具名插槽 -->
<h3>具名插槽</h3>
<Child2>
<template v-slot:footer><div>我是底部</div></template>
<template #header><div>我是头部</div></template>
<template v-slot:default>
<div>我是内容</div>
</template>
</Child2>
<div class="child">
<slot name="header"></slot>
<slot></slot>
<div>Hello, I am from Child.</div>
<slot name="footer"></slot>
</div>
<div class="child">
<div>Hello, I am from Child.</div>
<!-- 将user和callMe通过 v-bind 的方式传递 -->
<slot :user="user" :callMe="callMe"></slot>
</div>
<!--然后在父组件中的插槽内,通过类似 v-slot:default="slotProps" 接受子组件传递过来的数据-->
<Child3>
<!-- slotProps 可以自定义-->
<template v-slot:default="slotProps">
<div>我的名字:{{slotProps.user.name}}</div>
<div>我的年龄:{{slotProps.user.age}}</div>
<button @click="slotProps.callMe">Clicl Me</button>
</template>
</Child3>
<!-- 以上 slotProps 可以自定义,而且可以使用解构赋值的语法 -->
<!-- 解构赋值 -->
<template v-slot:other="{ user, callMe}">
<div>我的名字:{{user.name}}</div>
<div>我的年龄:{{user.age}}</div>
<button @click="callMe">Clicl Me</button>
</template>
-
动态的指令参数
<template>
...
<my-button @[someEvent]="handleSomeEvent()"/>
...
</template>
<script>
...
data(){
return{
...
someEvent: someCondition ? "click" : "dblclick"
}
},
methods:{
handleSomeEvent(){
// do something
}
}
...
</script>
- hookEvent 的使用
比如,我们调用了一个很耗费性能的第三方组件 List,这个组件可能需要渲染很久,为了更好的用户体验,我们想在 List 组件进行更新的时候添加一个 loading 的动画效果
<List @hook:updated="handleTableUpdated"></List >
另外,我们还可以通过下面的方式给一个 Vue 组件添加生命周期处理函数
vm.$on('hooks:created', cb)
vm.$once('hooks:created', cb)
mounted(){
const thirdPartyPlugin = thirdPartyPlugin()
this.$on('hook:beforeDestroy',()=>{
thirdPartyPlugin.destroy()
})
}
- key 值的使用
- CSS scoded 和深度作用选择器
- watch immediate deep $watch
这里 watch 的一个特点是,最初绑定的时候是不会执行的,要等到 id 改变时才执行监听计算。这可能导致我们页面第一次渲染出错
watch: {
id: {
handler(newValue) {
this.getDetails(newValue);
},
// 代表在wacth里声明了id后这个方法之后立即先去执行handler方法
immediate: true
// 通过指定deep属性为true, watch会监听对象里面每一个值的变化
deep: true
}
}
但是如果要在页面初始化时候加载数据,我们还需要在created或者mounted生命周期钩子里面再次调用$_loadData方法。不过,现在可以不用这样写了,通过配置watch的立即触发属性,就可以满足需求了
我们可以在需要的时候通过this.$watch来监听数据变化。那么如何取消监听呢,上例中this.$watch返回了一个值unwatch,是一个函数,在需要取消的时候,执行 unwatch()即可取消
export default {
data() {
return {
formData: {
name: '',
age: 0
}
}
},
created() {
this.$_loadData()
},
methods: {
// 模拟异步请求数据
$_loadData() {
setTimeout(() => {
// 先赋值
this.formData = {
name: '子君',
age: 18
}
// 等表单数据回填之后,监听数据是否发生变化
const unwatch = this.$watch(
'formData',
() => {
console.log('数据发生了变化')
},
{
deep: true
}
)
// 模拟数据发生了变化
setTimeout(() => {
this.formData.name = '张三'
}, 1000)
}, 1000)
}
}
}
- v-once 和 v-pre 提升性能
- v-cloak 解决页面闪烁问题
// template 中
<div class="#app" v-cloak>
<p>{{value.name}}</p>
</div>
// css 中
[v-cloak] {
display: none;
}
- 表单输入控制——表单修饰符/change事件/filter
<input v-model.number="age" type="number">
<input v-model.trim="msg">
给表单绑定事件,在事件处理中进行表单输入控制
<input v-model="value2" type="text" @change="inputChange(value2)" />
methods: {
inputChange: function(val) {
if (!val) return ''
val = val.toString()
this.value2 = val.charAt(0).toUpperCase() + val.slice(1)
}
}
filter:
<input v-model="value1" type="text" />
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
watch: {
value1(val) {
this.value1 = this.$options.filters.capitalize(val);
}
}
- 事件:特殊变量 $event
<!-- 注意这里调用的方法有两个参数 -->
<input v-model="value1" @change="inputChange('hello', $event)">
methods: {
inputChange(msg, e) {
console.log(msg, e);
}
}
在自定义事件中,$event 是从其子组件中捕获的值
场景:你想监听 el-input 的传递过来的值的同时,传递其他的参数。
<el-input
v-model="value2"
@change="change($event, 'hello')"
placeholder="Input something here"
/>
methods: {
change(e, val) {
console.log("event is " + e); // el-input 输入的值
console.log(val); // hello
}
}
- 调试template
// 这里最好是判断一下,只有在测试环境中才使用
// main.js
Vue.prototype.$log = window.console.log;
// 组件内部
<div>{{$log(info)}}</div>
继续肝
- require.context()
const path = require('path')
const files = require.context('@/components/home', false, /\.vue$/)
const modules = {}
files.keys().forEach(key => {
const name = path.basename(key, '.vue')
modules[name] = files(key).default || files(key)
})
components:modules
- .sync
// 父组件
<home :title.sync="title" />
//编译时会被扩展为
<home : @update:/>
// 子组件
// 所以子组件可以通过$emit 触发 update 方法改变
mounted(){
this.$emit("update:title", '这是新的title')
}
- .EventBus
1.就是声明一个全局Vue实例变量 EventBus , 把所有的通信数据,事件监听都存储到这个变量上; 2.类似于 Vuex。但这种方式只适用于极小的项目 3.原理就是利用on和on和emit 并实例化一个全局 vue 实现数据共享
// 在 main.js
Vue.prototype.$eventBus=new Vue()
// 传值组件
this.$eventBus.$emit('eventTarget','这是eventTarget传过来的值')
// 接收组件
this.$eventBus.$on("eventTarget",v=>{
console.log('eventTarget',v);//这是eventTarget传过来的值
})
- 路由传参
// 路由定义
{
path: '/describe/:id',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
path: `/describe/${id}`,
})
// 页面获取
this.$route.params.id
// 路由定义
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
// 页面获取
this.$route.params.id
// 路由定义
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
path: '/describe',
query: {
id: id
`}
)
// 页面获取
this.$route.query.id
- 路由按需加载
{
path:'/',
name:'home',
components:()=>import('@/components/home')
}
import()方法由es6提出,import()方法是动态加载,返回一个Promise对象,then方法的参数是加载到的模块。
类似于Node.js的require方法,主要import()方法是异步加载的。
- 动态组件
<transition>
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
</transition>
- components和 Vue.component
components:局部注册组件
export default{
components:{home}
}
Vue.component:全局注册组件
Vue.component('home',home)
- Vue.extend
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{extendData}}</br>实例传入的数据为:{{propsExtend}}</p>',//template对应的标签最外层必须只有一个标签
data: function () {
return {
extendData: '这是extend扩展的数据',
}
},
props:['propsExtend']
})
// 创建的构造器可以挂载到元素上,也可以通过 components 或 Vue.component()注册使用
// 挂载到一个元素上。可以通过propsData传参.
new Profile({propsData:{propsExtend:'我是实例传入的数据'}}).$mount('#app-extend')
// 通过 components 或 Vue.component()注册
Vue.component('Profile',Profile)
- mixins
场景:有些组件有些重复的 js 逻辑,如校验手机验证码,解析时间等,mixins 就可以实现这种混入 mixins 值是一个数组
const mixin = {
created(){
this.dealTime()
},
methods:{
dealTime(){
console.log('这是mixin的dealTime里面的方法')
}
}
}
export default{
mixins:[mixin]
}
- Vue.vue()
- install
var MyPlugin = {};
MyPlugin.install = function (Vue, options) {
// 2. 添加全局资源,第二个参数传一个值默认是update对应的值
Vue.directive('click', {
bind(el, binding, vnode, oldVnode) {
//做绑定的准备工作,添加时间监听
console.log('指令my-directive的bind执行啦');
},
inserted: function(el){
//获取绑定的元素
console.log('指令my-directive的inserted执行啦');
},
update: function(){
//根据获得的新值执行对应的更新
//对于初始值也会调用一次
console.log('指令my-directive的update执行啦');
},
componentUpdated: function(){
console.log('指令my-directive的componentUpdated执行啦');
},
unbind: function(){
//做清理操作
//比如移除bind时绑定的事件监听器
console.log('指令my-directive的unbind执行啦');
}
})
// 3. 注入组件
Vue.mixin({
created: function () {
console.log('注入组件的created被调用啦');
console.log('options的值为',options)
}
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
console.log('实例方法myMethod被调用啦');
}
}
//调用MyPlugin
Vue.use(MyPlugin,{someOption: true })
//3.挂载
new Vue({
el: '#app'
});
- Vue.nextTick
mounted(){ //因为 mounted 阶段 dom 并未渲染完毕,所以需要$nextTick
this.$nextTick(() => {
this.$refs.inputs.focus() //通过 $refs 获取dom 并绑定 focus 方法
})
}
- vue.derective
// 全局定义
Vue.directive("change-color",function(el,binding,vnode){
el.style["color"]= binding.value;
})
// 使用
<template>
<div v-change-color=“color”>{{message}}</div>
</template>
<script>
export default{
data(){
return{
color:'green'
}
}
}
</script>
- vue.filter
// 使用
// 在双花括号中
{{ message | capitalize }}
// 在 `v-bind` 中
<div v-bind:id="rawId | formatId"></div>
// 全局注册
Vue.filter('stampToYYMMDD', (value) =>{
// 处理逻辑
})
// 局部注册
filters: {
stampToYYMMDD: (value)=> {
// 处理逻辑
}
}
// 多个过滤器全局注册
// /src/common/filters.js
let dateServer = value => value.replace(/(\d{4})(\d{2})(\d{2})/g, '$1-$2-$3')
export { dateServer }
// /src/main.js
import * as custom from './common/filters/custom'
Object.keys(custom).forEach(key => Vue.filter(key, custom[key]))
- 事件修饰符
- 缓存和动画
注:匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配
- 全局路由钩子
// 1.router.beforeEach
router.beforeEach((to, from, next) => {
console.log('全局前置守卫:beforeEach -- next需要调用') //一般登录拦截用这个,也叫导航钩子守卫
if (path === '/login') {
next()
return
}
if (token) {
next();
}
})
- 组件路由钩子
1.beforeRouteEnter 在渲染该组件的对应路由被确认前调用,用法和参数与router.beforeEach类似,next需要被主动调用 此时组件实例还未被创建,不能访问this 可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数
beforeRouteEnter (to, from, next) {
// 这里还无法访问到组件实例,this === undefined
next( vm => {
// 通过 `vm` 访问组件实例
})
}
2.beforeRouteUpdate (v 2.2+) 在当前路由改变,并且该组件被复用时调用,可以通过this访问实例, next需要被主动调用,不能传回调
3.beforeRouteLeave 导航离开该组件的对应路由时调用,可以访问组件实例 this,next需要被主动调用,不能传回调
- Vue.routerVue.route
this.$router.push():跳转到不同的url,但这个方法回向history栈添加一个记录,点击后退会返回到上一个页面
this.$router.replace():不会有记录
this.$router.go(n):n可为正数可为负数。正数前进, 负数后退,类似 window.history.go(n)
- router-view 的 key
场景:由于 Vue 会复用相同组件, 即 /page/1 => /page/2 或者 /page?id=1 => /page?id=2 这类链接跳转时, 将不在执行created, mounted之类的钩子
<router-view :key="$route.fullPath"></router-view>
这样组件的 created 和 mounted 就都会执行
- vue-loader
它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件。简而言之,webpack 和 Vue Loader 的结合为你提供了一个现代、灵活且极其强大的前端工作流,来帮助撰写 Vue.js 应用。
- img 加载失败
场景:有些时候后台返回图片地址不一定能打开,所以这个时候应该加一张默认图片
// page 代码
<img :src="imgUrl" @error="handleError" >
<script>
export default{
data(){
return{
imgUrl:''
}
},
methods:{
handleError(e){
e.target.src=reqiure('图片路径') //当然如果项目配置了transformToRequire,参考上面 33.2
}
}
}
</script>
- 基础指令
- vue-loader是什么?使用它的用途有哪些?
vue 文件的一个加载器,将 template/js/style
转换成 js
模块。
用途:js
可以写 es6
、 style
样式可以 scss
或 less
、 template
可以加 jade
等
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!