当前位置: 首页 > news >正文

做网站用是内网穿透好国外产品设计网站

做网站用是内网穿透好,国外产品设计网站,安徽省住房和城乡建设厅证件查询,建网站程序工具第一部分:基础概念理解1. 什么是状态?用生活中的例子来理解想象你家里的电灯开关:状态:灯是开着的还是关着的状态变化:当你按下开关,灯的状态从"关"变成"开"界面更新:灯泡亮…

第一部分:基础概念理解

1. 什么是状态?用生活中的例子来理解

想象你家里的电灯开关:

  • 状态:灯是开着的还是关着的
  • 状态变化:当你按下开关,灯的状态从"关"变成"开"
  • 界面更新:灯泡亮起来(相当于UI重新绘制)

在Android应用中也是一样:

// 这就是一个简单的状态:按钮是否被点击过
var isClicked = false// 当用户点击按钮时,状态改变
isClicked = true// UI需要根据新状态更新显示

2. 为什么普通变量不能用?

让我们看一个错误的例子:

@Composable
fun BadCounter() {var count = 0  // 普通变量Column {Text("计数: $count")Button(onClick = { count++  // 这样做没用!println("count现在是: $count")  // 这行会打印,说明变量确实变了}) {Text("点击+1")}}
}

问题:虽然count的值确实增加了,但屏幕上的文字不会更新。为什么?

原因:Compose不知道要重新绘制界面,因为它不知道count变了。

3. 正确的方式:使用Compose的状态

@Composable
fun GoodCounter() {// 使用 remember + mutableStateOf 创建Compose能够监控的状态var count by remember { mutableStateOf(0) }Column {Text("计数: $count")Button(onClick = { count++  // 现在有用了!}) {Text("点击+1")}}
}

关键理解

  • mutableStateOf(0):创建一个初始值为0的可变状态
  • remember:让这个状态在界面重新绘制时不会丢失
  • by:Kotlin的委托属性,让我们可以直接使用count而不是count.value

第二部分:深入理解状态的生命周期

1. remember vs rememberSaveable 的区别

让我们用一个完整的例子来理解:

@Composable
fun CounterComparison() {// 使用 remember:屏幕旋转后会重置为0var normalCount by remember { mutableStateOf(0) }// 使用 rememberSaveable:屏幕旋转后保持不变var savedCount by rememberSaveable { mutableStateOf(0) }Column(modifier = Modifier.padding(16.dp)) {Card(modifier = Modifier.padding(8.dp)) {Column(modifier = Modifier.padding(16.dp)) {Text("普通计数器 (remember)")Text("值: $normalCount", style = MaterialTheme.typography.headlineMedium)Button(onClick = { normalCount++ }) {Text("增加普通计数")}}}Card(modifier = Modifier.padding(8.dp)) {Column(modifier = Modifier.padding(16.dp)) {Text("持久计数器 (rememberSaveable)")Text("值: $savedCount", style = MaterialTheme.typography.headlineMedium)Button(onClick = { savedCount++ }) {Text("增加持久计数")}}}Text("尝试旋转屏幕看看区别!",style = MaterialTheme.typography.bodyMedium,modifier = Modifier.padding(top = 16.dp))}
}

实验:运行这个代码,点击两个按钮几次,然后旋转屏幕。你会发现:

  • normalCount回到了0
  • savedCount保持了原来的值

2. 状态的作用域理解

@Composable
fun StateScope() {// 这个状态属于 StateScope 函数var parentState by remember { mutableStateOf("父级状态") }Column {Text("父级: $parentState")// 子组件1ChildComponent1()// 子组件2  ChildComponent2()Button(onClick = { parentState = "已更新" }) {Text("更新父级状态")}}
}@Composable
fun ChildComponent1() {// 这个状态只属于 ChildComponent1var localState by remember { mutableStateOf(0) }Text("子组件1的本地状态: $localState")Button(onClick = { localState++ }) {Text("更新子组件1")}
}@Composable
fun ChildComponent2() {// 这个状态只属于 ChildComponent2var localState by remember { mutableStateOf(0) }Text("子组件2的本地状态: $localState")Button(onClick = { localState++ }) {Text("更新子组件2")}
}

关键理解:每个组件的状态是独立的,子组件无法直接访问父组件的状态。

第三部分:状态提升(State Hoisting)详解

1. 问题场景:子组件之间需要共享状态

想象一个购物车场景:

// 有问题的设计:各自管理自己的状态
@Composable
fun ProblemShopping() {Column {ProductItem(name = "苹果", price = 5.0)ProductItem(name = "香蕉", price = 3.0)// 问题:总价应该显示在哪里?每个商品都不知道其他商品的状态}
}@Composable
fun ProductItem(name: String, price: Double) {var quantity by remember { mutableStateOf(0) }Row {Text("$name - ¥$price")Text("数量: $quantity")Button(onClick = { quantity++ }) { Text("+") }Button(onClick = { if(quantity > 0) quantity-- }) { Text("-") }}
}

2. 解决方案:状态提升

// 数据类定义
data class CartItem(val name: String,val price: Double,val quantity: Int = 0
)// 解决方案:将状态提升到父组件
@Composable
fun GoodShopping() {// 状态在父组件中管理var cartItems by remember {mutableStateOf(listOf(CartItem("苹果", 5.0),CartItem("香蕉", 3.0),CartItem("橙子", 4.0)))}// 计算总价val totalPrice = cartItems.sumOf { it.price * it.quantity }Column(modifier = Modifier.padding(16.dp)) {Text("购物车", style = MaterialTheme.typography.headlineLarge)// 显示每个商品cartItems.forEachIndexed { index, item ->ProductItemStateless(item = item,onQuantityChange = { newQuantity ->// 更新特定商品的数量cartItems = cartItems.toMutableList().apply {this[index] = item.copy(quantity = newQuantity)}})}Divider(modifier = Modifier.padding(vertical = 8.dp))// 显示总价Text("总价: ¥${"%.2f".format(totalPrice)}",style = MaterialTheme.typography.headlineMedium,fontWeight = FontWeight.Bold)}
}// 无状态组件:只负责显示和触发事件
@Composable
fun ProductItemStateless(item: CartItem,onQuantityChange: (Int) -> Unit
) {Card(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) {Row(modifier = Modifier.padding(16.dp),horizontalArrangement = Arrangement.SpaceBetween,verticalAlignment = Alignment.CenterVertically) {Column {Text(item.name, style = MaterialTheme.typography.bodyLarge)Text("¥${item.price}", style = MaterialTheme.typography.bodyMedium)}Row(verticalAlignment = Alignment.CenterVertically) {Button(onClick = { if (item.quantity > 0) {onQuantityChange(item.quantity - 1)}}) {Text("-")}Text("${item.quantity}",modifier = Modifier.padding(horizontal = 16.dp),style = MaterialTheme.typography.bodyLarge)Button(onClick = { onQuantityChange(item.quantity + 1) }) {Text("+")}}}}
}

关键理解

  1. 状态上移:将需要共享的状态移到共同的父组件
  2. 数据向下流:父组件将状态作为参数传给子组件
  3. 事件向上流:子组件通过回调函数通知父组件更新状态

第四部分:ViewModel与屏幕级状态管理

1. 什么时候需要ViewModel?

当你的状态涉及:

  • 网络请求
  • 数据库操作
  • 复杂的业务逻辑
  • 需要在配置更改后保持的数据

2. 详细的ViewModel示例

// 1. 定义UI状态数据类
data class TodoUiState(val todos: List<Todo> = emptyList(),val isLoading: Boolean = false,val error: String? = null,val newTodoText: String = ""
)data class Todo(val id: String = UUID.randomUUID().toString(),val text: String,val isCompleted: Boolean = false,val createdAt: Long = System.currentTimeMillis()
)// 2. ViewModel管理业务逻辑和状态
class TodoViewModel(private val todoRepository: TodoRepository = TodoRepository()
) : ViewModel() {// 私有可变状态private val _uiState = MutableStateFlow(TodoUiState())// 公开只读状态val uiState: StateFlow<TodoUiState> = _uiState.asStateFlow()init {loadTodos()}// 加载待办事项fun loadTodos() {viewModelScope.launch {_uiState.update { it.copy(isLoading = true, error = null) }try {delay(1000) // 模拟网络延迟val todos = todoRepository.getAllTodos()_uiState.update { it.copy(todos = todos,isLoading = false)}} catch (e: Exception) {_uiState.update { it.copy(error = "加载失败: ${e.message}",isLoading = false)}}}}// 更新新待办事项的文本fun updateNewTodoText(text: String) {_uiState.update { it.copy(newTodoText = text) }}// 添加新的待办事项fun addTodo() {val currentText = _uiState.value.newTodoText.trim()if (currentText.isBlank()) returnviewModelScope.launch {try {val newTodo = Todo(text = currentText)todoRepository.addTodo(newTodo)_uiState.update { currentState ->currentState.copy(todos = currentState.todos + newTodo,newTodoText = "")}} catch (e: Exception) {_uiState.update { it.copy(error = "添加失败: ${e.message}")}}}}// 切换待办事项完成状态fun toggleTodoCompleted(todoId: String) {viewModelScope.launch {try {val updatedTodos = _uiState.value.todos.map { todo ->if (todo.id == todoId) {todo.copy(isCompleted = !todo.isCompleted)} else {todo}}todoRepository.updateTodos(updatedTodos)_uiState.update { it.copy(todos = updatedTodos) }} catch (e: Exception) {_uiState.update { it.copy(error = "更新失败: ${e.message}")}}}}// 删除待办事项fun deleteTodo(todoId: String) {viewModelScope.launch {try {val updatedTodos = _uiState.value.todos.filter { it.id != todoId }todoRepository.updateTodos(updatedTodos)_uiState.update { it.copy(todos = updatedTodos) }} catch (e: Exception) {_uiState.update { it.copy(error = "删除失败: ${e.message}")}}}}// 清除错误消息fun clearError() {_uiState.update { it.copy(error = null) }}
}// 3. 模拟的Repository
class TodoRepository {private var todos = mutableListOf<Todo>()suspend fun getAllTodos(): List<Todo> {// 模拟网络延迟delay(500)return todos.toList()}suspend fun addTodo(todo: Todo) {delay(200)todos.add(todo)}suspend fun updateTodos(newTodos: List<Todo>) {delay(200)todos.clear()todos.addAll(newTodos)}
}

3. 在Compose中使用ViewModel

@Composable
fun TodoScreen(viewModel: TodoViewModel = viewModel()
) {// 收集状态val uiState by viewModel.uiState.collectAsStateWithLifecycle()// 处理错误显示uiState.error?.let { error ->LaunchedEffect(error) {// 可以显示Snackbar或其他错误提示// snackbarHostState.showSnackbar(error)viewModel.clearError()}}Column(modifier = Modifier.padding(16.dp)) {// 标题Text("待办事项",style = MaterialTheme.typography.headlineLarge,modifier = Modifier.padding(bottom = 16.dp))// 添加新待办事项的输入框AddTodoSection(newTodoText = uiState.newTodoText,onTextChange = viewModel::updateNewTodoText,onAddClick = viewModel::addTodo)Spacer(modifier = Modifier.height(16.dp))// 加载状态if (uiState.isLoading) {Box(modifier = Modifier.fillMaxWidth(),contentAlignment = Alignment.Center) {CircularProgressIndicator()}} else {// 待办事项列表LazyColumn {items(items = uiState.todos,key = { it.id }) { todo ->TodoItem(todo = todo,onToggleCompleted = { viewModel.toggleTodoCompleted(todo.id) },onDelete = { viewModel.deleteTodo(todo.id) })}}}// 刷新按钮Button(onClick = viewModel::loadTodos,modifier = Modifier.fillMaxWidth().padding(top = 16.dp)) {Text("刷新")}}
}@Composable
fun AddTodoSection(newTodoText: String,onTextChange: (String) -> Unit,onAddClick: () -> Unit
) {Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.spacedBy(8.dp)) {OutlinedTextField(value = newTodoText,onValueChange = onTextChange,label = { Text("新的待办事项") },modifier = Modifier.weight(1f))Button(onClick = onAddClick,enabled = newTodoText.isNotBlank()) {Text("添加")}}
}@Composable
fun TodoItem(todo: Todo,onToggleCompleted: () -> Unit,onDelete: () -> Unit
) {Card(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) {Row(modifier = Modifier.padding(16.dp),horizontalArrangement = Arrangement.SpaceBetween,verticalAlignment = Alignment.CenterVertically) {Row(modifier = Modifier.weight(1f),verticalAlignment = Alignment.CenterVertically) {Checkbox(checked = todo.isCompleted,onCheckedChange = { onToggleCompleted() })Text(text = todo.text,modifier = Modifier.padding(start = 8.dp),textDecoration = if (todo.isCompleted) {TextDecoration.LineThrough} else {TextDecoration.None},color = if (todo.isCompleted) {Color.Gray} else {Color.Unspecified})}IconButton(onClick = onDelete) {Icon(imageVector = Icons.Default.Delete,contentDescription = "删除",tint = Color.Red)}}}
}

第五部分:StateHolder模式详解

1. 为什么需要StateHolder?

ViewModel很好,但有时候我们需要更细粒度的状态管理:

// 场景:一个复杂的搜索功能
@Composable
fun ComplexSearchScreen() {// 如果所有状态都放在一个ViewModel里,会变得很庞大// 我们可以将不同的状态分离到不同的StateHolder中val searchStateHolder = remember { SearchStateHolder() }val filterStateHolder = remember { FilterStateHolder() }val historyStateHolder = remember { HistoryStateHolder() }// 使用各个StateHolder...
}

2. SearchStateHolder完整实现

// 搜索状态数据类
data class SearchState(val query: String = "",val results: List<SearchResult> = emptyList(),val isSearching: Boolean = false,val suggestions: List<String> = emptyList()
)data class SearchResult(val id: String,val title: String,val description: String,val url: String
)// SearchStateHolder类
class SearchStateHolder {private val _state = MutableStateFlow(SearchState())val state: StateFlow<SearchState> = _state.asStateFlow()private val searchJob = Job()private val scope = CoroutineScope(Dispatchers.Main + searchJob)// 搜索建议的数据源private val commonSuggestions = listOf("Android开发", "Kotlin教程", "Jetpack Compose","Material Design", "Android架构", "MVVM模式")fun updateQuery(newQuery: String) {_state.update { it.copy(query = newQuery) }updateSuggestions(newQuery)// 如果查询不为空,延迟搜索if (newQuery.isNotBlank()) {scope.launch {delay(500) // 防抖动if (_state.value.query == newQuery) {performSearch(newQuery)}}} else {_state.update { it.copy(results = emptyList()) }}}private fun updateSuggestions(query: String) {val suggestions = if (query.isBlank()) {emptyList()} else {commonSuggestions.filter { it.contains(query, ignoreCase = true) }.take(5)}_state.update { it.copy(suggestions = suggestions) }}private fun performSearch(query: String) {scope.launch {_state.update { it.copy(isSearching = true) }try {delay(1000) // 模拟网络请求// 模拟搜索结果val results = (1..10).map { index ->SearchResult(id = "result_$index",title = "搜索结果 $index: $query",description = "这是关于 $query 的搜索结果描述...",url = "https://example.com/result$index")}_state.update { it.copy(results = results,isSearching = false)}} catch (e: Exception) {_state.update { it.copy(isSearching = false,results = emptyList())}}}}fun selectSuggestion(suggestion: String) {updateQuery(suggestion)}fun clearSearch() {_state.update { SearchState() }}fun dispose() {searchJob.cancel()}
}// FilterStateHolder类
data class FilterState(val selectedCategory: String = "全部",val dateRange: DateRange? = null,val sortBy: SortOption = SortOption.RELEVANCE
)enum class SortOption(val displayName: String) {RELEVANCE("相关性"),DATE("日期"),POPULARITY("热度")
}data class DateRange(val start: Long,val end: Long
)class FilterStateHolder {private val _state = MutableStateFlow(FilterState())val state: StateFlow<FilterState> = _state.asStateFlow()private val categories = listOf("全部", "技术", "设计", "产品", "管理", "其他")fun getCategories() = categoriesfun selectCategory(category: String) {_state.update { it.copy(selectedCategory = category) }}fun setSortOption(option: SortOption) {_state.update { it.copy(sortBy = option) }}fun setDateRange(range: DateRange?) {_state.update { it.copy(dateRange = range) }}fun clearFilters() {_state.update { FilterState() }}
}

3. 在Compose中使用StateHolder

@Composable
fun SearchScreen() {// 创建StateHolder实例val searchStateHolder = remember { SearchStateHolder() }val filterStateHolder = remember { FilterStateHolder() }// 收集状态val searchState by searchStateHolder.state.collectAsState()val filterState by filterStateHolder.state.collectAsState()// 清理资源DisposableEffect(Unit) {onDispose {searchStateHolder.dispose()}}Column(modifier = Modifier.fillMaxSize()) {// 搜索栏SearchBar(searchState = searchState,onQueryChange = searchStateHolder::updateQuery,onSuggestionClick = searchStateHolder::selectSuggestion)// 过滤器FilterSection(filterState = filterState,onCategorySelect = filterStateHolder::selectCategory,onSortChange = filterStateHolder::setSortOption)// 搜索结果SearchResults(searchState = searchState,filterState = filterState)}
}@Composable
fun SearchBar(searchState: SearchState,onQueryChange: (String) -> Unit,onSuggestionClick: (String) -> Unit
) {Column {OutlinedTextField(value = searchState.query,onValueChange = onQueryChange,label = { Text("搜索...") },modifier = Modifier.fillMaxWidth(),trailingIcon = {if (searchState.isSearching) {CircularProgressIndicator(modifier = Modifier.size(20.dp),strokeWidth = 2.dp)}})// 搜索建议if (searchState.suggestions.isNotEmpty()) {LazyColumn(modifier = Modifier.heightIn(max = 200.dp)) {items(searchState.suggestions) { suggestion ->Text(text = suggestion,modifier = Modifier.fillMaxWidth().clickable { onSuggestionClick(suggestion) }.padding(12.dp))}}}}
}@Composable
fun FilterSection(filterState: FilterState,onCategorySelect: (String) -> Unit,onSortChange: (SortOption) -> Unit
) {Column(modifier = Modifier.padding(16.dp)) {Text("分类", style = MaterialTheme.typography.titleMedium)LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp),modifier = Modifier.padding(vertical = 8.dp)) {items(listOf("全部", "技术", "设计", "产品", "管理", "其他")) { category ->FilterChip(selected = filterState.selectedCategory == category,onClick = { onCategorySelect(category) },label = { Text(category) })}}Text("排序", style = MaterialTheme.typography.titleMedium)Row(horizontalArrangement = Arrangement.spacedBy(8.dp),modifier = Modifier.padding(vertical = 8.dp)) {SortOption.values().forEach { option ->FilterChip(selected = filterState.sortBy == option,onClick = { onSortChange(option) },label = { Text(option.displayName) })}}}
}@Composable
fun SearchResults(searchState: SearchState,filterState: FilterState
) {when {searchState.isSearching -> {Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center) {CircularProgressIndicator()}}searchState.results.isEmpty() && searchState.query.isNotBlank() -> {Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center) {Text("没有找到相关结果")}}else -> {LazyColumn {items(searchState.results) { result ->SearchResultItem(result = result)}}}}
}@Composable
fun SearchResultItem(result: SearchResult) {Card(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp)) {Column(modifier = Modifier.padding(16.dp)) {Text(text = result.title,style = MaterialTheme.typography.titleMedium,fontWeight = FontWeight.Bold)Text(text = result.description,style = MaterialTheme.typography.bodyMedium,modifier = Modifier.padding(top = 4.dp))Text(text = result.url,style = MaterialTheme.typography.bodySmall,color = Color.Blue,modifier = Modifier.padding(top = 4.dp))}}
}

这样的实现有什么好处?

  1. 职责分离:每个StateHolder只管理自己的状态
  2. 可复用性:SearchStateHolder可以在其他屏幕中使用
  3. 易于测试:每个StateHolder都可以独立测试
  4. 易于维护:修改搜索逻辑不会影响过滤逻辑

第六部分: 实际项目应用建议

6.1 项目结构建议

app/
├── ui/
│   ├── screens/
│   └── components/
├── state/
│   ├── holders/
│   ├── events/
│   └── ApplicationStateStore.kt
├── data/
│   └── repositories/
└── di/└── modules/

6.2 状态管理选择指南

  • 简单UI状态:使用remember + mutableStateOf
  • 需要保持配置更改:使用rememberSaveable
  • 复杂域状态:使用StateHolder模式
  • 屏幕级业务逻辑:使用ViewModel
  • 全局共享状态:结合StateHolder + CompositionLocal
  • 大型应用:使用Application State Store

6.3 常见问题和解决方案

问题1:状态在配置更改后丢失

// 解决方案:使用rememberSaveable
var state by rememberSaveable { mutableStateOf(initialValue) }

问题2:过度重组导致性能问题

// 解决方案:使用derivedStateOf和稳定的数据结构
val derivedState by remember { derivedStateOf { computeExpensiveValue() } }

问题3:测试困难

// 解决方案:使用接口和依赖注入
interface StateHolder {val state: StateFlow<State>
}

总结

Jetpack Compose的状态管理涉及多个层面,从简单的本地UI状态到复杂的全局应用状态。关键是要根据具体需求选择合适的模式:

  1. 状态提升:让组件更可复用和可测试
  2. StateHolder模式:管理复杂域状态的理想选择
  3. ViewModel集成:处理屏幕级业务逻辑
  4. CompositionLocal:全局状态的便捷访问方式
  5. 事件驱动:让状态更新更可预测

通过合理运用这些模式和最佳实践,可以构建出高性能、可维护的Jetpack Compose应用。良好的状态管理不仅关乎技术实现,更关乎应用的整体架构设计。


文章转载自:

http://2knwsx5B.tqpr.cn
http://ipCvLbwG.tqpr.cn
http://rEJnZ8Ec.tqpr.cn
http://j3hd3QZa.tqpr.cn
http://eeO0wF9j.tqpr.cn
http://NP4BucJa.tqpr.cn
http://6D5odOje.tqpr.cn
http://j8N5hF7B.tqpr.cn
http://SPLlaSE9.tqpr.cn
http://7YSdaW91.tqpr.cn
http://04BNEJBQ.tqpr.cn
http://zuG0znh5.tqpr.cn
http://KU5qztVy.tqpr.cn
http://cz0TZY7N.tqpr.cn
http://SD6mOupG.tqpr.cn
http://ajDJhmvw.tqpr.cn
http://tF4D5Rrs.tqpr.cn
http://RiTEcI7q.tqpr.cn
http://HmE6aucJ.tqpr.cn
http://hRcORFYk.tqpr.cn
http://bF9SFNlg.tqpr.cn
http://RB8JcFgB.tqpr.cn
http://DtVmojHU.tqpr.cn
http://PId9NHwW.tqpr.cn
http://DgDayV8s.tqpr.cn
http://B070NHd6.tqpr.cn
http://YuDWNAJ4.tqpr.cn
http://zIkibqL5.tqpr.cn
http://tOILXwsB.tqpr.cn
http://j51W4Wyr.tqpr.cn
http://www.cdong.cn/news/345/

相关文章:

  • 佛山论坛建站模板优秀手机网站案例
  • 个人可以做几个网站制作网页时经常使用什么对网页的布局进行控制
  • 网站制作技术河北建设工程信息网停用公告
  • 如何进入谷歌网站东莞南城网站建设公司怎么样
  • 重庆网上注册公司网站教做黏土手工的网站
  • 邢台做网站改版网站建设的财务分析
  • 公司请做网站易语言做网站爆破工具
  • 江苏优化网站公司哪家好佛山高端网站建设报价
  • 桂林网站优化价格手机网站建设浩森宇特
  • 品牌购买网站集团网站建设流程
  • 二级网站建设思路百度合伙人官网app
  • 曾经做网站网站代理windows优化大师怎么使用
  • 网站建设网络推广方案ppt合肥瑶海区地图
  • 肇庆网站设计asp 女性 美容 知识 网站 源码
  • 网站开发涉及内容做推广如何引流
  • 万网速成网站企业销售网站
  • 北京移动端网站多少钱今天的新闻内容50字
  • 网站wap版怎么做爱站网官网查询域名
  • 制作网站收费网站常见程序问题
  • 石家庄网站优化排名推广大连软件开发网站建设
  • 客户网站建设什么网站做设计可以赚钱
  • 武进网站建设要多少钱网站地图怎么添加
  • 博学云网站建设wordpress 访问速度
  • 商城网站推广方案做一婚恋网站多少钱
  • 网站建设怎么估算费用和报价万州网络科技有限公司
  • 做网站价格海报制作软件免费版
  • 合肥做网站的企业郑州网站建设方案服务公司
  • 英文购物网站模板求推荐在哪个网站做德语翻译员
  • 网站建设步骤 高清教 程猪八戒托管赏金做网站
  • 外贸手机商城网站建设 深圳免费咨询法律律师电话号码