星星充电 前端开发工程师 一面
一、自我介绍
二、实习项目
三、Uniapp转小程序
四、css定位方式
五、typeof计算null结果
六、前端跨域问题处理
七、页面内存飙升是什么导致的
八、当前是在哪里
问了很简单的问题,面了约15分钟结束了,然后秒挂
继续阅读->问了很简单的问题,面了约15分钟结束了,然后秒挂
继续阅读->computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
watch: {
someData(newVal, oldVal) {
// 数据变化时执行的操作
}
}
Echarts 实例可通过如 setOption
等方法更新图表数据和配置。例如,先获取 Echarts 实例 let myChart = echarts.init(dom)
,然后在数据变化时 myChart.setOption({...})
来更新图表显示。
update
方法可用于更新 Echarts 实例的配置项和数据。例如,在某些场景下可以直接调用 chart.update({...})
来更新图表部分配置或数据,但要注意配置项的结构和数据的对应关系,不然可能导致图表显示异常。
Vue 的状态主要是响应式数据,存储在 data
函数返回的对象中,这些数据变化会触发视图更新。也可以通过 Vuex 或 Pinia 等状态管理库管理全局状态,方便多组件共享和修改数据。
state
(存储状态)、mutations
(同步修改状态)、actions
(可包含异步操作来提交 mutations)、getters
(获取派生状态)等核心概念。例如:const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
})
store.$patch
等方式直接修改状态,同时有更好的类型推断支持。例如:import { defineStore } from 'pinia';
const useStore = defineStore('main', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
}
})
可以通过在 Vue 原型上挂载属性来设置全局数据,例如 Vue.prototype.$globalData = {... }
,这样在所有组件中都可以通过 this.$globalData
访问,但要注意数据的变更可能不易追踪和管理,适用于一些全局的配置信息等。
<template>
<div>
<ul>
<li v-for="(todo, index) in todos" :key="index">
<input type="checkbox" v-model="todo.done">
<span :class="{ 'done': todo.done }">{{ todo.text }}</span>
</li>
</ul>
<input type="text" v-model="newTodoText" placeholder="添加新事项">
<button @click="addTodo">添加</button>
</div>
</template>
<script>
export default {
data() {
return {
todos: [],
newTodoText: ''
}
},
methods: {
addTodo() {
if (this.newTodoText) {
this.todos.push({ text: this.newTodoText, done: false });
this.newTodoText = '';
}
}
}
}
</script>
<style>
.done {
text-decoration: line-through;
}
</style>
可以使用快慢指针法。定义两个指针,慢指针每次移动一步,快指针每次移动两步。如果链表有环,快指针最终会追上慢指针;如果快指针到达链表末尾(null
),则链表无环。
function hasCycle(head) {
let slow = head;
let fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
return true;
}
}
return false;
}
在单链表有环的情况下,快慢指针一定会相遇。因为快指针每次比慢指针多走一步,随着循环的进行,它们之间的距离会逐渐缩小,最终必然会重合。
Cache-Control
和 Expires
等头部字段控制,浏览器直接从缓存中读取资源,不向服务器发送请求。例如 Cache-Control: max-age=3600
表示资源在 3600 秒内有效。Last-Modified
和 ETag
等字段判断资源是否有更新。如果没有更新,服务器返回 304 状态码,浏览器继续使用缓存资源;如果有更新,则返回新资源。script.js?v=1.0.1
或 script.123456.js
,每次更新文件时修改版本号或哈希值,强制浏览器重新下载资源。Cache-Control: no-cache
或 Cache-Control: must-revalidate
等头部信息,让浏览器在每次访问资源时都向服务器验证资源是否更新。fetch
事件,根据策略返回缓存资源或从网络获取最新资源并更新缓存。v-model
等指令方便地实现数据与视图的双向同步;React 是单向数据绑定,数据流动更明确,需要开发者手动处理数据更新时的视图更新。export default {
created() {
axios.get('https://api.example.com/data')
.then(response => {
this.data = response.data;
})
.catch(error => {
console.error('Error fetching data:', error);
});
}
}
Access-Control-Allow-Origin: https://client.example.com
.center {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 100px;
margin-top: -50px; /* 高度的一半 */
margin-left: -100px; /* 宽度的一半 */
}
.container {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.rotate {
animation: rotateAnimation 2s linear infinite;
}
@keyframes rotateAnimation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
console.log('start');
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise');
});
console.log('end');
function mergeSortedArrays(arr1, arr2) {
const result = [];
let i = 0, j = 0;
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
result.push(arr1[i++]);
} else {
result.push(arr2[j++]);
}
}
return result.concat(i < arr1.length? arr1.slice(i) : []).concat(j < arr2.length? arr2.slice(j) : []);
}
const array1 = [1, 3, 5];
const array2 = [2, 4, 6];
console.log(mergeSortedArrays(array1, array2));
// 输出: [1, 2, 3, 4, 5, 6]
height
和line - height
值相等,就可以实现垂直居中。例如:div {
height: 100px;
line - height: 100px;
}
div
中的文本就会在垂直方向上居中。display: table - cell
,并且设置vertical - align: middle
,可以让子元素垂直居中。例如:.parent {
display: table - cell;
vertical - align: middle;
}
display: flex
时,使用align - items: center
可以让子元素在交叉轴(通常是垂直方向)上居中。例如:.container {
display: flex;
align - items: center;
}
justify - content: center
和align - items: center
。place - items: center
可以让子元素在网格单元格中垂直和水平都居中。例如:.grid - container {
display: grid;
place - items: center;
}
align - items: center
(垂直居中)和justify - items: center
(水平居中)。1
,3.14
。可以进行数学运算,如加法、减法等。'hello'
,"world"
。可以进行字符串拼接、截取等操作。true
和false
,用于条件判断,比如if
语句中。null
。undefined
。{name: 'John', age: 30}
,对象的属性可以通过点语法(obj.name
)或者方括号语法(obj['name']
)访问。[1, 2, 'three']
,可以通过索引访问数组元素,如arr[0]
。function add(a, b) {
return a + b;
}
{
let x = 10;
console.log(x);
}
console.log(x); // 报错,x在块外不可访问
const PI = 3.14;
PI = 3.15; // 报错
// 普通函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => a + b;
this
,它会继承外层作用域的this
,这在处理回调函数中的this
指向问题时很有用。const name = 'John';
const greeting = `Hello, ${name}!`;
console.log(greeting);
+
来拼接字符串。// 数组解构
const [a, b] = [1, 2];
// 对象解构
const {x, y} = {x: 10, y: 20};
const source = {a: 1, b: 2};
const target = {};
Object.assign(target, source);
console.log(target); // {a: 1, b: 2}
const source = {a: {x: 1}, b: 2};
const target = {};
Object.assign(target, source);
source.a.x = 2;
console.log(target.a.x); // 2,因为只是浅拷贝,内部对象引用相同
const source = {a: {x: 1}, b: 2};
const target = JSON.parse(JSON.stringify(source));
source.a.x = 2;
console.log(target.a.x); // 1,深拷贝后互不影响
const arr = ["a", "b", "a", "c"];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // ["a", "b", "c"]
new Set(arr)
创建一个 Set 对象,它会自动去除重复元素,然后通过扩展运算符...
将 Set 对象转换回数组。const arr = ["a", "b", "a", "c"];
const uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
if (!uniqueArr.includes(arr[i])) {
uniqueArr.push(arr[i]);
}
}
console.log(uniqueArr); // ["a", "b", "c"]
const numbers = [1, 2, 1, 3];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3]
const numbers = [1, 2, 1, 3];
const uniqueNumbers = [];
for (let i = 0; i < numbers.length; i++) {
if (!uniqueNumbers.includes(numbers[i])) {
uniqueNumbers.push(numbers[i]);
}
}
console.log(uniqueNumbers); // [1, 2, 3]
async/await
来处理异步结果。name
、age
、email
等属性。email
属性创建索引,这样就可以通过email
快速查找用户。function throttle(func, delay) {
let timer = null;
return function() {
if (!timer) {
func.apply(this, arguments);
timer = setTimeout(() => {
timer = null;
}, delay);
}
};
}
timer
为null
时才执行原始函数func
,然后设置一个定时器,在delay
时间后将timer
重置为null
,这样就限制了函数的执行频率。function debounce(func, delay) {
let timer = null;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this, arguments);
}, delay);
};
}
setInterval
函数来实现轮询请求。假设我们有一个fetchData
函数用于发送请求获取数据:function fetchData() {
// 这里是发送请求的代码,例如使用fetch API
return fetch('https://example.com/api/data')
.then(response => response.json());
}
setInterval(fetchData, 2000);
fetchData
函数发送请求。不过要注意,在页面关闭时应该清除这个定时器,避免不必要的请求和内存占用。可以使用clearInterval
函数来清除。requests
对象来存储请求相关的信息:const requests = {};
let requestId = 0;
function sendRequest() {
const id = requestId++;
requests[id] = {status: 'pending'};
// 发送请求,假设使用fetch API
fetch('https://example.com/api/data')
.then(response => response.json())
.then(data => {
// 根据标识符更新对应的请求状态和数据
if (requests[id]) {
requests[id].status = 'fulfilled';
requests[id].data = data;
}
});
return id;
}
axios.interceptors.request.use((config) => {
// 在请求头中添加认证信息
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
axios.interceptors.response.use((response) => {
// 假设服务器返回的数据格式是{data: {actualData}},提取实际数据
return response.data.data;
}, (error) => {
// 处理错误,比如401错误(未授权)
if (error.response && error.response.status === 401) {
// 跳转到登录页面或者进行重新认证操作
window.location.href = '/login';
}
return Promise.reject(error);
});
axios.all
和axios.spread
来处理并发请求的结果。例如:axios.all([axios.get('/api/user'), axios.get('/api/posts')])
.then(axios.spread((userResponse, postsResponse) => {
// 同时获取用户信息和帖子信息后的处理
const user = userResponse.data;
const posts = postsResponse.data;
}));
props
属性。在父组件中,在使用子组件标签时,通过属性绑定的方式传递数据。例如,<template>
<ChildComponent :message="parentMessage" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentMessage: 'Hello from parent'
};
}
};
</script>
props
选项来接收数据,像这样:<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: ['message']
};
</script>
$emit
方法触发自定义事件,并传递数据。例如:<template>
<button @click="sendDataToParent">Send Data</button>
</template>
<script>
export default {
methods: {
sendDataToParent() {
const data = 'Data from child';
this.$emit('childData', data);
}
}
};
</script>
<template>
<ChildComponent @childData="handleChildData" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleChildData(data) {
console.log(data);
}
}
};
</script>
event-bus.js
:import Vue from 'vue';
export default new Vue();
$emit
触发事件来传递数据,例如:<template>
<button @click="sendData">Send Data</button>
</template>
<script>
import eventBus from './event-bus.js';
export default {
methods: {
sendData() {
const data = 'Data to share';
eventBus.$emit('sharedData', data);
}
}
};
</script>
$on
来监听事件获取数据,比如:<script>
import eventBus from './event-bus.js';
export default {
mounted() {
eventBus.$on('sharedData', (data) => {
console.log(data);
});
}
};
</script>
mapState
等辅助函数来获取数据,通过commit
或dispatch
等方式来修改数据。data
选项,是响应式的,组件可以通过mapState
等辅助函数获取并使用这些状态数据。例如:const store = new Vuex.Store({
state: {
count: 0
}
});
state
对象作为第一个参数,通过提交mutation
来同步地修改状态,并且有严格的类型限制,便于调试和追踪状态变化。例如:mutations: {
increment(state) {
state.count++;
}
}
mutation
来间接改变状态。例如:actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
}
}
state
中派生出一些新的数据,方便组件获取和使用经过处理后的状态数据。例如:getters: {
doubleCount: state => state.count * 2
}
defineStore
函数来定义一个仓库,里面可以包含状态、操作状态的方法等。例如:import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
}
});
mutation
那样严格区分同步和异步操作的概念,操作状态的方法可以直接在actions
中处理异步逻辑,更加简洁灵活。mutation
的严格同步限制),开发者可以更直接地定义和操作状态。Composition API
等,而 Vuex 虽然也能用于 Vue 3,但 Pinia 在这方面的集成感觉更流畅。同时,Pinia 也能通过插件支持 Vue 2,具有更好的跨版本使用能力。data
选项可以看作是 Model 的一部分,存储着组件的数据;模板(template
)就是 View,负责展示数据;而 Vue 实例本身以及其内部的计算属性、方法等就相当于 ViewModel,它通过双向绑定等机制,实现了数据和视图的自动关联和更新。例如,当在data
中修改一个属性的值时,与之绑定的模板中的内容会自动更新,反之,用户在视图中输入等操作导致的数据变化,也会同步更新到data
中的对应属性上。Object.defineProperty()
方法来劫持数据的读写操作。在 Vue 实例初始化时,会对data
选项中的数据进行遍历,使用Object.defineProperty()
为每个属性添加get
和set
访问器属性。例如:function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
return value;
},
set: function(newValue) {
if (newValue!== value) {
value = newValue;
// 当数据变化时,通知相关的依赖(如模板中的绑定表达式等)进行更新
}
}
});
}
get
函数,当修改数据时,会触发set
函数,在set
函数中可以进行更新视图等相关操作。get
函数被触发时,会收集依赖(通常是一些更新函数),当数据发生变化触发set
函数时,会遍历这些依赖并执行它们,实现视图的更新。这其实就是一种发布 - 订阅模式,数据变化是发布者,依赖的更新函数是订阅者,当有新的数据变化消息(发布)时,对应的订阅者(依赖的更新函数)就会被通知并执行更新操作。{{ message }}
这样的绑定表达式,当解析模板时,读取message
数据就会触发其get
函数进行依赖收集,后续如果message
的值改变,就会通过之前收集的依赖来更新模板中对应的部分。Object.defineProperty()
对data
等选项中的数据进行处理,使其变为响应式数据,也就是让数据的读写能够被 Vue 感知并进行相应的处理。例如,在 Vue 组件的初始化阶段:function initData(vm) {
let data = vm.$options.data;
data = vm._data = typeof data === 'function'? data.call(vm) : data;
// 遍历_data中的属性,使其成为响应式
Object.keys(data).forEach(key => {
defineReactive(vm._data, key, data[key]);
});
}
set
函数,在set
函数中会通过之前收集的依赖(如Watcher
实例,它代表了对某个数据的依赖,通常由模板解析、计算属性等创建)来通知相关的更新操作。比如,一个组件的data
中的属性变化了,会找到所有依赖这个属性的Watcher
,然后执行Watcher
的更新函数,这个更新函数可能会触发虚拟 DOM 的重新渲染等操作,最终将更新后的内容反映到真实的 DOM 上,实现组件的更新。class Logger {
constructor() {
if (!Logger.instance) {
Logger.instance = this;
}
return Logger.instance;
}
log(message) {
console.log(message);
}
}
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2); // true,说明获取到的是同一个实例
new Logger()
创建实例,得到的都是同一个Logger
实例,方便统一管理日志记录。function createShape(type, options) {
switch (type) {
case 'circle':
return new Circle(options.radius);
case'rectangle':
return new Rectangle(options.width, options.height);
default:
throw new Error('Invalid shape type');
}
}
class Circle {
constructor(radius) {
this.radius = radius;
}
}
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
const circle = createShape('circle', { radius: 5 });
const rectangle = createShape('rectangle', { width: 10, height: 20 });
createShape
这个工厂函数,根据传入的类型参数来创建不同的图形对象,使用者不需要知道具体的对象创建细节。class NewsPublisher {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
notify(news) {
this.observers.forEach(observer => observer.update(news));
}
}
class NewsSubscriber {
update(news) {
console.log(`Received news: ${news}`);
}
}
const publisher = new NewsPublisher();
const subscriber1 = new NewsSubscriber();
const subscriber2 = new NewsSubscriber();
publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);
publisher.notify('Breaking news!');
NewsPublisher
)维护着订阅者列表,当有新新闻时,通过notify
函数通知所有订阅者(NewsSubscriber
),订阅者接收到通知后执行update
函数进行相应的处理。git clone <repository-url>
命令从远程仓库(如 GitHub、GitLab 等)克隆代码到本地。例如,要克隆一个名为my-project
的仓库,命令如下:git clone https://github.com/username/my-project.git
git add <file(s)>
命令将文件添加到暂存区,准备提交。可以添加单个文件,如git add index.html
,也可以添加所有修改的文件,用git add.
表示添加当前目录下所有修改的文件。git commit -m "<commit-message>"
命令将暂存区的文件提交到本地仓库,<commit-message>
是对本次提交内容的简要描述,例如:git commit -m "Fix a bug in the login page"
git push origin <branch-name>
命令将本地仓库的更改推送到远程仓库对应的分支上,通常origin
是远程仓库的默认别名,<branch-name>
是分支名称,比如:git push origin master
git branch <new-branch-name>
命令创建一个新的分支,例如:git branch feature-branch
git checkout <branch-name>
命令切换到指定的分支,如:git checkout feature-branch
git checkout -b <new-branch-name>
命令同时创建并切换到新分支。feature-branch
分支合并到master
分支,首先切换到master
分支(git checkout master
),然后使用git merge feature-branch
命令进行合并。git status
git status
命令可以查看当前工作目录的状态,比如哪些文件被修改了、哪些文件是新增的、当前处于哪个分支等信息。例如:git status
执行后会输出类似如下内容:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
这表明当前在master
分支,index.html
文件被修改了但还没添加到暂存区。
git log
命令可以查看提交历史记录,它会显示每次提交的作者、时间、提交信息以及对应的哈希值等内容,方便追踪项目的变更情况。例如:git log
输出类似如下:
commit 123456789abcdef (HEAD -> master)
Author: Your Name <your@email.com>
Date: Mon Dec 16 10:00:00 2024
Add new feature to the project
commit 987654321fedcba
Author: Another Author <another@email.com>
Date: Fri Dec 13 14:30:00 2024
Fix a bug in the login page
可以添加一些参数来更方便地查看日志,比如--oneline
可以让每条提交记录显示在一行,更简洁直观:
git log --oneline
git diff
命令用于查看文件修改前后的差异内容,能帮助了解具体做了哪些代码改动。如果想查看工作目录中已修改但未暂存文件与暂存区版本的差异,可以直接执行:git diff
若想查看已暂存文件与上次提交版本的差异,使用:
git diff --cached
示例
:比如计算一个班级学生成绩的平均分。可以通过以下步骤实现:
scores = [80, 90, 75, 85, 95] # 假设的学生成绩数组
total_score = 0
for score in scores:
total_score += score
average_score = total_score / len(scores)
print(average_score)
这里就是按照顺序依次执行各个操作来完成计算平均分这个任务,整个过程注重的是操作步骤的顺序和执行。
Student
类:class Student:
def __init__(self, score):
self.score = score
def get_score(self):
return self.score
然后创建学生对象的列表,计算平均分:
students = [Student(80), Student(90), Student(75), Student(85), Student(95)]
total_score = 0
for student in students:
total_score += student.get_score()
average_score = total_score / len(students)
print(average_score)
这里把学生抽象成了Student
类,每个学生对象有自己的成绩属性以及获取成绩的方法,通过对象的交互(调用get_score
方法获取成绩并累加)来完成计算平均分的任务,并且可以方便地对Student
类进行扩展,比如添加更多关于学生的属性和方法等。
区别
:
适用场景
:
主体部分
:
<button bindtap="login" disabled="{{isLoggingIn}}" class="{{isLoggingIn?'disabled-button' : 'normal-button'}}">登录</button>
<view wx:if="{{isLoggingIn}}">
<image src="/images/loading.gif" style="width:20px;height:20px"></image>
</view>
在对应的 JavaScript 逻辑中:
Page({
data: {
isLoggingIn: false
},
login() {
this.setData({
isLoggingIn: true
});
// 这里发送登录请求,假设使用wx.request
wx.request({
url: 'https://example.com/login',
success: (res) => {
// 登录成功处理
this.setData({
isLoggingIn: false
});
},
fail: (res) => {
// 登录失败处理
this.setData({
isLoggingIn: false
});
}
});
}
});
wx.onAppRoute((res) => {
const route = res.path;
if (route === '/pages/login/login' && isLoggedIn) { // isLoggedIn表示是否已登录,可根据存储的登录凭证判断
wx.redirectTo({
url: '/pages/home/home' // 已登录就重定向到首页等已登录页面
});
}
});
1. 讲一下微信小程序中登录是怎么实现的
原理概述
具体步骤
wx.login()
方法获取用户登录凭证(code)。例如: wx.login({
success: function (res) {
if (res.code) {
// 将code发送到服务器
wx.request({
url: 'https://yourserver.com/login',
data: {
code: res.code
},
method: 'POST',
success: function (response) {
// 处理服务器返回的用户信息等
}
});
} else {
console.log('获取用户登录态失败!' + res.errMsg);
}
}
});
https://api.weixin.qq.com/sns/jscode2session
接口发送请求,需要传入小程序的appid
、secret
和收到的code
。示例代码(以 Node.js 为例): const https = require('https');
const appid = 'YOUR_APPID';
const secret = 'YOUR_SECRET';
function getOpenId(code) {
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const result = JSON.parse(data);
resolve(result.openid);
} catch (e) {
reject(e);
}
});
}).on('error', (err) => {
reject(err);
});
});
}
2. 谈一下你对小程序的理解
定义和特点
开发模式
view
)、基础内容组件(如text
)等。应用场景
3. Echart底层原理你了解吗
数据驱动
渲染流程
drawRect
用于绘制矩形,beginPath
和lineTo
等用于绘制折线等)将图形绘制在 Canvas 上。如果是 SVG 渲染,它会生成相应的 SVG 元素(如<rect>
、<line>
等)来构建图表。交互原理
mouseover
事件)来触发相应的交互行为。例如,显示数据提示框,这个数据提示框的内容是根据当前鼠标位置对应的数据点来动态生成的。4. 图表刷新、点击穿透、回流重构等问题
图表刷新
myChart
是已经初始化的图表实例,newData
是新的数据,可以通过以下方式刷新: myChart.setOption({
series: [{
data: newData
}]
});
点击穿透
@click.stop
(在 Vue 中)或者event.stopPropagation()
(在原生 JavaScript 中)来阻止事件冒泡。例如在 Vue 中: <div class="overlay" @click.stop>
<!-- 模态框内容 -->
</div>
回流重构
position:absolute
或display:none
),进行操作后再将其恢复,来减少回流次数。5. 你简历上谈到了跨域问题处理,你是怎么处理的
跨域产生的原因
http://localhost:8080
访问,而后端 API 是在http://api.example.com
提供服务,就会产生跨域。解决方法(以 Node.js 后端为例)
express
框架可以这样设置: const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header('Access - Control - Allow - Origin', '*');
res.header('Access - Control - Allow - Methods', 'GET, POST, PUT, DELETE');
res.header('Access - Control - Allow - Headers', 'Content - Type, Authorization');
next();
});
vue.config.js
文件中可以这样设置: module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://api.example.com',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
6. Vue 中监听和计算属性的区别
计算属性(computed)
fullName
,它依赖于firstName
和lastName
: const app = Vue.createApp({
data() {
return {
firstName: 'John',
lastName: 'Doe'
};
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
});
监听属性(watch)
const app = Vue.createApp({
data() {
return {
inputValue: ''
};
},
watch: {
inputValue(newValue, oldValue) {
console.log(`输入值从 ${oldValue} 变为 ${newValue}`);
}
}
});
7. Vue3 和 Vue2 的区别
性能方面
Object.defineProperty
,Proxy 可以代理整个对象,而不是像Object.defineProperty
那样只能代理对象的已有属性。这使得在添加或删除属性时也能被响应式地追踪,并且在性能上有一定的提升,特别是在处理大型复杂对象时。API 设计
setup
函数来组合多个逻辑相关的代码块,包括数据、方法、生命周期钩子等。相比 Vue2 主要基于选项式 API(data
、methods
、mounted
等选项),组合式 API 提供了更灵活的代码组织方式。模板语法
<template>
<div>第一个元素</div>
<div>第二个元素</div>
</template>
8. Vue 组件间通信
父子组件通信
props
)将数据传递给子组件。例如,父组件中有一个数据message
,在子组件中通过props
接收: <!-- 父组件 -->
<template>
<child-component :message="message"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
message: 'Hello from parent'
};
}
};
</script>
<!-- 子组件 -->
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: ['message']
};
</script>
<!-- 子组件 -->
<template>
<button @click="sendData">发送数据</button>
</template>
<script>
export default {
methods: {
sendData() {
this.$emit('data - from - child', 'Data from child');
}
}
};
</script>
<!-- 父组件 -->
<template>
<child-component @data - from - child="handleData"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleData(data) {
console.log(data);
}
}
};
</script>
非父子组件通信(事件总线或 Vuex)
EventBus.js
文件: import Vue from 'vue';
export const eventBus = new Vue();
然后在组件 A 中发送事件:
import { eventBus } from './EventBus.js';
eventBus.$emit('custom - event', 'Data to share');
在组件 B 中接收事件:
import { eventBus } from './EventBus.js';
created() {
eventBus.$on('custom - event', (data) => {
console.log(data);
});
}
commit
方法触发mutations
来修改状态,通过dispatch
方法触发actions
来执行异步操作并修改状态。例如,在store.js
文件中定义一个简单的 Vuex store: import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
asyncIncrement(context) {
setTimeout(() => {
context.commit('increment');
}, 1000);
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
}
});
在组件中使用:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment']),
...mapActions(['asyncIncrement'])
}
};
</script>
9. Vue 两种路由以及区别
Hash 模式
location.hash
来实现路由的。location.hash
的值就是 URL 中#
后面的部分。当#
后面的值发生变化时,浏览器不会向服务器发送请求,而是会触发hashchange
事件。例如,http://example.com/#/home
中的#/home
就是hash
值。hash
值在 URL 中比较明显,不太美观。History 模式
history.pushState()
和history.replaceState()
方法来改变 URL,并且不会引起页面刷新。例如,通过history.pushState({}, '', '/home')
可以将当前 URL 修改为http://example.com/home
。同时,当用户点击浏览器的前进或后退按钮时,会触发popstate
事件,根据这个事件可以实现路由的切换。http://example.com/about
)时,服务器需要返回正确的页面(通常是单页应用的index.html
),否则会出现 404 错误。10. 实现页面元素垂直居中
方法一:使用 Flexbox 布局
display: flex
,并使用align-items: center
和justify-content: center
属性,可以轻松地将子元素在水平和垂直方向上居中。例如,对于一个简单的 HTML 结构: <div class="parent">
<div class="child">
这是要居中的内容
</div>
</div>
CSS 样式如下:
.parent {
display: flex;
justify-content: center;
align-items: center;
height: 300px; /* 假设父元素有一定高度 */
}
方法二:使用 CSS Grid 布局
display: grid
,然后使用place-items: center
属性来同时在水平和垂直方向上居中子元素。例如: <div class="grid - parent">
<div class="grid - child">
这是要居中的内容
</div>
</div>
CSS 样式:
.grid - parent {
display: grid;
place-items: center;
height: 300px; /* 假设父元素有一定高度 */
}
方法三:绝对定位和负边距
position: relative
),然后子元素使用绝对定位(position: absolute
),并通过设置top
、left
属性为50%
将元素的左上角定位到父元素的中心,最后使用负边距(margin - top
和margin - left
)将元素向回拉回自身高度和宽度的一半,从而实现居中。例如: <div class="parent - abs">
<div class="child - abs">
这是要居中的内容
</div>
</div>
CSS 样式:
.parent - abs {
position: relative;
height: 300px;
}
.child - abs {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 100px;
margin - top: - 50px;
margin - left: - 100px;
}
方法四:使用transform
属性结合绝对定位
top
和left
设置为50%
,然后使用transform: translate(-50%, -50%)
来将元素在水平和垂直方向上移动自身宽度和高度的一半,实现居中。例如: <div class="parent - trans">
<div class="child - trans">
这是要居中的内容
</div>
</div>
CSS 样式:
.parent - trans {
position: relative;
}
.child - trans {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
继续阅读->