`
netskys
  • 浏览: 48511 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

poi 解析百M Excel ,bs架构 在线概算导入

阅读更多
  今天碰上有人问POI的性能问题  哎,当初哥们做导入概算数据为POI 头疼了N久,终归功夫不负苦心人,最后解决了如下问题
  1 excel 概算文件超大 近 100m (我测试用了个200m的 都不会 OOM  哈哈)
  2 要检查excel中的 #REF 等错误计算公式,因为要入库,同时需要检查 数据的合法性 如 Number类型对象数字等
  3 因为 几万条数据 太过繁琐,所以需要 一次性报告给用户 所有的错误,并且定位错误行列,以供用户差错
  4 允许用户对单独sheet的批量导入  比如 5个sheet 用户高兴倒几个就倒几个 高兴倒哪个就倒哪个
   5 因是web架构,解析时提供 进度条提示 否则180m 几乎5分钟客户会crazy

哎 对此解决问题如下   最下提供代码仅供参考
  1  以前用poi 通用的user model 解析2m以下的还凑合对大数据 直接OOM,经官方查询发现 有个Event modle 并供有demo 小试一下下  ,very goode 详情参考http://poi.apache.org/spreadsheet/how-to.html#event_api

2 没办法 只能对每个cell进行验证 不过大家肯定发愁 我如何知道 excel的列对应 db的哪个column 啊?呵呵 对于这点 哥们我提出了规则 给用户个 匹配管理页面,让用户自己去定义 excel的哪个sheet 对应哪个表  哪个列对用哪个column  当然 在此肯定少不了用反射,哈哈我的最爱,自己 db的 type length 等 以及 entity 的property  这些对应是麻烦点 不过 可以连水平哦
3 报错给用户 这个肯定不能一碰上错 就alert,这样 不友好,也不现实,最好的就是 把所有的错误给用户生成一个文件,可供在线打开以及下载,客户对照着文件改excel  哈哈 要是你愿意的话 就把excel 的cell全称红色也行,不过 那就没办法提示用户 这行 的错误信息, 比如 “xxx表 1100行 20列 xx错误,应xxx ” 那么 在这里就只能用我们的异常机制了,不合法的抛出异常,自己捕获处理,写文件,render view 都可
4 既然用户想选择 那肯定 要给用户提供一个 复选框吧,当然如果 sheet个数不定  那就只能靠ajax 去捞 复选框的个数了,哈哈比较麻烦,但是道理很简单, 选哪个解析哪个啊

5 幸好前台用的ext 找个 process tip 很容易,不过关键是 如何切分任务来定制进度条的进度,刚开始想到了监听进程执行百分比,不过jdk不知道提供这个接口否,问 n多高人,都不知道,其实想想一个线程执行的百分比确实难以预测啊,毕竟cpu 的轮回,况且时关系,我就用了简单的自定制任务比,整个任务量100  完成某个阶段是多少 然后在不同的过程中 ++  呵呵 任务量放在 http session 里,前台的 进度条通过定时器触发ajax 呵呵


呵呵 下面是我写的一些 解析excel的代码 很乱啊  小心点砸
package com.hollysys.ipmip.util;

import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.EOFRecord;

import com.hollysys.ipmip.common.CommonDeclare;
import com.hollysys.ipmip.entity.business.estimateimport.EstimateAttributes;
import com.hollysys.ipmip.entity.business.estimateimport.EstimateCalc;
import com.hollysys.ipmip.exceptions.ImportEstimateException;
import com.hollysys.ipmip.service.business.estimateimport.EstimateImportService;

public class EventModelUtil implements HSSFListener {
	private SSTRecord sstrec;
	//存放所选sheet
	private  List<Integer> sheetNums = new ArrayList<Integer>();
	//当前的sheet序号
	private int sheetNum = 0;
	//保存excel的实际sheet个数
	private int count = 0;
	//存放sheet的map
	private Map<Integer, EstimateCalc> sheetMap = null;
	//判断首行
	private int firstRowNum = 0;
	//最终存放首行
	private int firstRowFinal = 0;
	// 存放导入配置文件
	private Map<Integer, Map<Integer, String>> typeRelationMap = null;
	// 存放表的详细信息
	private Map<Integer, Map<String, EstimateAttributes>> tableAttribMap = null;
	// 存放异常信息
	private Exception exception=null;
	private EstimateImportService estimateImportService;
	private HttpSession session;
	private Long userId;
	private OutputStreamWriter fw=null;
	private int countLine=1;
	private Random random=new Random();
	//存放保存数据库间断个数
//	private int bound=0;
	private Logger logger=Logger.getLogger(EventModelUtil.class);
	/**
	 * This method listens for incoming records and handles them as required.
	 * 
	 * @param record
	 *            The record that was found while reading.
	 */
	public EventModelUtil() {

	}

	public EventModelUtil(List<Integer> sheetNums,
			Map<Integer, Map<Integer, String>> typeRelationMap,
			Map<Integer, Map<String, EstimateAttributes>> tableAttribMap,
			Exception e, EstimateImportService estimateImportService,
			HttpSession session, Long userId,OutputStreamWriter fw) {
		this.sheetNums = sheetNums;
		this.typeRelationMap = typeRelationMap;
		this.tableAttribMap = tableAttribMap;
		this.exception = e;
		this.estimateImportService = estimateImportService;
		this.session = session;
		this.userId = userId;
		this.fw=fw;
	}

	@SuppressWarnings("static-access")
	public void processRecord(Record record) {
		java.text.DecimalFormat formatter2 = new java.text.DecimalFormat("#.00");
		if (record.getSid() == BOFRecord.sid) {
			BOFRecord bof = (BOFRecord) record;
			if (bof.getType() == bof.TYPE_WORKBOOK) {
				String str="3,4,5,6";
				changeSession(session, CommonDeclare.ESTIMATE_PROCESS_CONST, Integer.valueOf(createRandom(str, false)), CommonDeclare.ESTIMATE_UPFLAG_ADD_CONST);
				logger.info("新workBook" + bof.getRecordSize());
			} else if (bof.getType() == bof.TYPE_WORKSHEET) {
				logger.info("新的sheet:" + sheetNum);
				bof.getRecordSize();
				isRightSheetNum();
				sheetMap = new LinkedHashMap<Integer, EstimateCalc>();
				sheetNum++;
			}
		}
		try {
			if (record.getSid() == EOFRecord.sid) {
				if (sheetNum > 0) {
					//取出解析的最后一个sheet
					int lastSheetNum=sheetNums.get(sheetNums.size()-1);
					int count=sheetNum*Integer.valueOf(createRandom("19,20,21", false))-10;
					if(sheetNum==lastSheetNum){
						count=Integer.valueOf(createRandom("90,91,92", false));
					}
					changeSession(session, CommonDeclare.ESTIMATE_PROCESS_CONST, count, CommonDeclare.ESTIMATE_UPFLAG_ADD_CONST);
					logger.info("当前sheet首行:" + firstRowFinal);
					logger.info(sheetNum - 1 + "sheet解析结束");
					EOFRecord eof=(EOFRecord)record;
					eof.getRecordSize();
					if(sheetNums.contains(sheetNum - 1)&&sheetMap!=null){
						if(firstRowFinal<=0){
							String str="在"+CommonDeclare.tableName.get(sheetNum-1)+"中配置有问题,请重新配置";
							logger.error(str);
							throw new RuntimeException(str);
						}else{
							if(session.getAttribute("importExceptionMsg")==null||!session.getAttribute("importExceptionMsg").equals("1")){
								changeSession(session, CommonDeclare.ESTIMATE_PROCESS_CONST, Integer.valueOf(createRandom("1,2,3", false)), CommonDeclare.ESTIMATE_UPFLAG_UPDATE_CONST);
								estimateImportService.saveCurrentSheetToTempTable(sheetMap,sheetNum-1, session, userId);
							}
							sheetMap.clear();
						}
					}
					firstRowFinal=0;
					countLine=1;
				}
			}
			if (record.getSid() == BoundSheetRecord.sid) {
				//获得sheet个数
				count++;
			}
			if (record.getSid() == SSTRecord.sid) {
				sstrec = (SSTRecord) record;
			}

			if (sheetNums.contains(sheetNum - 1)) {
				getSwitchRecord(record, formatter2, sheetNum - 1,
						typeRelationMap.get(sheetNum - 1), tableAttribMap
								.get(sheetNum - 1), CommonDeclare.tableName
								.get(sheetNum - 1));
			}
		}catch(RuntimeException e){
			throw e;
		} 
		catch (Exception e) {
			e.printStackTrace();
			setException(e);
			session.setAttribute("importExceptionMsg", "1");
			try {
				if(countLine==1){
					String str="***********************"+CommonDeclare.tableName.get(sheetNum - 1)+"*******************************"+System.getProperty("line.separator");
					fw.write(str);
					logger.error(str);
				}
				String strDetial=countLine+":"+e.getMessage()+System.getProperty("line.separator");
				fw.write(strDetial);
				logger.error(strDetial);
				countLine++;
				fw.flush();
			} catch (Exception e1) {
				logger.error(e1.getMessage());
				e1.printStackTrace();
			}
		}
	}
	private void getSwitchRecord(Record record,
			java.text.DecimalFormat formatter2, int sheetNum,
			Map<Integer, String> typeRelation,
			Map<String, EstimateAttributes> tableAttrib, String tablename)
			throws Exception {
		EstimateCalc calc = null;
		try {
			switch (record.getSid()) {
			case RowRecord.sid:
				RowRecord rowrec = (RowRecord) record;
				int exclude=(sheetNum+1)*20;
				if(exclude>=100) exclude=99;
				changeSession(session, CommonDeclare.ESTIMATE_PROCESS_CONST, Integer.valueOf(createRandom("1,2", false)), CommonDeclare.ESTIMATE_UPFLAG_UPDATE_CONST,exclude);
				calc = (EstimateCalc) CommonDeclare.tempEstimateMClass.get(
						CommonDeclare.typeBrief.get(sheetNum)).newInstance();
				sheetMap.put(rowrec.getRowNumber(), calc);
				break;
			case NumberRecord.sid:
				NumberRecord numrec = (NumberRecord) record;
				Object obj = formatter2.format(numrec.getValue());
				//当为数字的时候判断首行
				if (numrec.getValue() == (numrec.getColumn() + 1.00)) {
					firstRowNum++;
				}
				if (firstRowNum == CommonDeclare.ESTIMATE_COLUMN_CONST) {
					firstRowFinal = numrec.getRow();
				}
				//第一列为序号 统一数字的格式
				if(firstRowFinal>0&&numrec.getColumn()==0){
					java.text.DecimalFormat formatter = new java.text.DecimalFormat("#");
					obj=formatter.format(numrec.getValue());
				}
				EstimateAttributes attrib=getAttrib(typeRelation, tableAttrib,(CellValueRecordInterface) numrec);
				//如果是poi读出是数字而实际上不是数字的就不做格式转化
				if(attrib!=null&&!attrib.getData_type().trim().equals("NUMBER")){
					obj=numrec.getValue();
					if(obj.toString().endsWith(".0")){
						obj=obj.toString().substring(0,obj.toString().indexOf("."));
					}
				}
				calc = sheetMap.get(numrec.getRow());
				lessColumnLength(obj, calc, tablename,
						(CellValueRecordInterface) numrec,attrib,getCellTitle(typeRelation, (CellValueRecordInterface) numrec));
				break;
			case LabelSSTRecord.sid:
				LabelSSTRecord lrec = (LabelSSTRecord) record;
				obj = sstrec.getString(lrec.getSSTIndex());
				if(obj!=null&&firstRowFinal==0&&isNumber(obj.toString().trim())){
					if (Long.valueOf(obj.toString()) == (lrec.getColumn() + 1.0)) {
						firstRowNum++;
					}
					if (firstRowNum == CommonDeclare.ESTIMATE_COLUMN_CONST) {
						firstRowFinal = lrec.getRow();
					}
				}else{
					firstRowNum = 0;
				}
				calc = sheetMap.get(lrec.getRow());
				isNumberColumn(obj,tablename, (CellValueRecordInterface) lrec,
						getAttrib(typeRelation, tableAttrib,
								(CellValueRecordInterface) lrec),getCellTitle(typeRelation, (CellValueRecordInterface) lrec));
				lessColumnLength(obj, calc, tablename,
						(CellValueRecordInterface) lrec, getAttrib(
								typeRelation, tableAttrib,
								(CellValueRecordInterface) lrec),getCellTitle(typeRelation, (CellValueRecordInterface) lrec));

				break;
			case BlankRecord.sid:
				firstRowNum = 0;
				break;
			case FormulaRecord.sid:
				FormulaRecord furd = (FormulaRecord) record;
				firstRowNum = 0;
				Double b = furd.getValue();
				Object tb = formatter2.format(b);
				calc=sheetMap.get(furd.getRow());
				lessColumnLength(tb, calc, tablename,
						(CellValueRecordInterface) furd, getAttrib(
								typeRelation, tableAttrib,
								(CellValueRecordInterface) furd),getCellTitle(typeRelation, (CellValueRecordInterface) furd));
				break;
			default:
				firstRowNum = 0;
			}
		} catch (ImportEstimateException e){
			logger.error(e.getMessage());
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 * 获得配置文件
	 */
	public EstimateAttributes getAttrib(Map<Integer, String> typeRelation,
			Map<String, EstimateAttributes> tableAttrib,
			CellValueRecordInterface record) {
		String str=typeRelation.get(record.getColumn() + 1);
		if(str!=null){
			return tableAttrib.get(str.substring(0,str.indexOf(",")));
		}else{
			return null;
		}
	}
	
	/**
	 * 获得cell 的标题
	 */
	public String getCellTitle(Map<Integer, String> typeRelation,CellValueRecordInterface record){
		String str=typeRelation.get(record.getColumn() + 1);
		if(str!=null){
			return str.substring(str.indexOf(",")+1);
		}else{
			return null;
		}
	}

	/**
	 * 判断cell里内容小于数据库字段的值
	 */
	public void lessColumnLength(Object obj, EstimateCalc calc,
			String tablename, CellValueRecordInterface record,
			EstimateAttributes attrib,String cellTitle) throws Exception {
		if(firstRowFinal>0&&attrib!=null){
			int length = 0;
			Pattern p = Pattern.compile("^[\u0080-\u07ff\u0800-\uffff]+$");
			if (obj != null && !obj.equals("")) {
				for (int k = 0; k < obj.toString().trim().length(); k++) {
					char c = obj.toString().trim().charAt(k);
					Matcher m = p.matcher(c + "");
					if (m.find()) {
						length += 2;
					} else {
						length++;
					}
				}
			}
			if (length <= attrib.getData_length()) {
					if(obj!=null&&!obj.toString().trim().equals("")){
						if(session.getAttribute("importExceptionMsg")==null||!session.getAttribute("importExceptionMsg").equals("1")){
							org.apache.commons.beanutils.BeanUtils.copyProperty(calc,
									getBeanProperyNameByTableField(attrib.getColumn_name()
											.trim()), obj);
						}
					}
			} else {
				String str="在【" +tablename + " "+cellTitle+"】中【"
				+ (record.getRow() + 1) + "行" + (record.getColumn() + 1)
				+ "列】内容为【"+obj+"】,字符长度【"+length+"】,应填写小于【" + attrib.getData_length() + "】位的内容";
				logger.error(str);
				throw new ImportEstimateException(str);
			}
		}
	}

	/**
	 * 应填写数字的列出现非数字
	 */
	public void isNumberColumn(Object obj,String tablename,
			CellValueRecordInterface record, EstimateAttributes attrib,String cellTitle)
			throws Exception {
		if(firstRowFinal>0&&attrib!=null){
			if(obj!=null){
				//将全角的转化成半角的 否则无法trim
				obj=obj.toString().replaceAll(" ", " ");
				obj = obj.toString().trim();
				if ("NUMBER".equals(attrib.getData_type().trim())&&!obj.toString().equals("")&&!isNumber(obj.toString())) {
					throw new ImportEstimateException("在【" + tablename + " "+cellTitle+"】中【"
							+ (record.getRow()+1) + "行" + (record.getColumn()+1) + "列】内容为【"+obj+"】,应填写数字");
				}
			}
		}
	}
	
	/**
	 * 判断是否为5个sheet
	 */
	public void isRightSheetNum(){
		if (count > 5) {
			String str="导入文件有多余的sheet或者存在隐藏sheet,请检查多出的 " + (count - 5)
			+ "个sheet";
			logger.error(str);
			throw new RuntimeException(str);
		}
		if (count < 5) {
			String str="导入文件格式不符合 矿、土、安、设备、其他 顺序格式, 五个sheet依次排序格式";
			logger.error(str);
			throw new RuntimeException(str);
		}
	}

	/**
	 * get Bean ProperyName By Table Field.
	 * 
	 * @param fieldName
	 */
	private String getBeanProperyNameByTableField(String fieldName) {
		fieldName = fieldName.toLowerCase();
		while (fieldName.indexOf("_") > 0) {
			int pos = fieldName.indexOf("_");
			fieldName = fieldName.substring(0, pos)
					+ fieldName.substring(pos + 1, pos + 2).toUpperCase()
					+ fieldName.substring(pos + 2);
		}
		return fieldName;
	}
	/**
	 * 判断是否为数字 
	 */
	public boolean isNumber(String str){
		//处理全角空格
		str=str.replaceAll(" ", " ");
		return Pattern.matches("^\\d+$|^\\d+\\.\\d+$", str.trim());
	}

	public void setException(Exception exception) {
		this.exception = exception;
	}
	
	/**
	 * 获得session  切改变session
	 * @param HttpSession session 当前用户session
	 * @param String attribute    session的key
	 * @param Object changeValue  session 的value
	 * @param String flag        1:新增 纯赋值 2:修改 在原来的基础上加 3:删除 
	 * @return
	 */
	public  static void changeSession(HttpSession session,String attribute,Object changeValue,String flag,int...exclude){
		if(session!=null&&attribute!=null&&!"".equals(attribute)&&!"".equals(flag)){
			
			if(CommonDeclare.ESTIMATE_UPFLAG_ADD_CONST.equals(flag)){
				session.setAttribute(attribute, changeValue);
			}else if(CommonDeclare.ESTIMATE_UPFLAG_UPDATE_CONST.equals(flag)){
				if(session.getAttribute(attribute)!=null){
					Object obj=session.getAttribute(attribute);
					if(changeValue  instanceof Integer){
						if(exclude!=null&&exclude.length>0){
							int i=0;
							for(int n:exclude){
								i=n;
							}
							if((Integer)obj>=i){
								session.setAttribute(attribute, i);
							}else{
								session.setAttribute(attribute, (Integer)obj+(Integer)changeValue);
							}
						}else{
							session.setAttribute(attribute, (Integer)obj+(Integer)changeValue);
						}
					}else if(changeValue  instanceof String){
						session.setAttribute(attribute, (String)obj+(String)changeValue);
					}
				}
			}else if(CommonDeclare.ESTIMATE_UPFLAG_DELETE_CONST.equals(flag)){
				if(session.getAttribute(attribute)!=null){
					session.removeAttribute(attribute);
				}
			}
		}
	}
	
	/**
	 * 根据要求随机生成随机数(字符)
	 * @param String str="1,2,3,...."or str="1234567" 字符串 如果是数组格式用逗号隔开
	 * @param boolean flag 是否把结果连接成串 true 连接 false 否 
	 * @param int ... n 可选参数 如果有值则按照要求截取 否则按照
	 */
	public static String createRandom(String str , boolean flag,int... n){
		String result="";
		if(str!=null&&!"".equals(str)){
			String[] array=str.split((str.indexOf(",")>-1)?",":"");
			Random random=new Random();
			int i=0;
			if(n!=null&&n.length>0){
				for(int j :n){
					i=j;
				}
			}else{
				i=array.length-1;
			}
			result=array[random.nextInt(i)];
			if(flag){
				StringBuilder builder=new StringBuilder();
				for(int m=0;m<i;m++){
					builder.append(result=array[random.nextInt(i)]);
				}
				result=builder.toString();
			}else{
				result=array[random.nextInt(i)];
			}
		}
		return result;
	}
}

分享到:
评论

相关推荐

    基于springboot+Javaweb的二手图书交易系统源码数据库文档.zip

    基于springboot+Javaweb的二手图书交易系统源码数据库文档.zip

    Linux课程设计.doc

    Linux课程设计.doc

    课程考试的概要介绍与分析

    课程考试资源描述 本资源是为应对各类课程考试而精心准备的综合性学习包。它包含了多门学科的考试指南、历年真题、模拟试题以及详细的答案解析。这些资源旨在帮助学生系统复习课程内容,理解考试要点,提高解题技巧,从而在考试中取得优异成绩。 资源中不仅包含了基础的考试资料,还特别加入了考试技巧讲解和备考策略分析。学生可以通过这些资源了解不同题型的解题方法和思路,学会如何在有限的时间内高效答题。此外,还有针对弱项科目和难点的专项训练,帮助学生攻克学习瓶颈。 为了确保资源的时效性和准确性,我们会定期更新考试资料和模拟试题,及时反映最新的考试动态和趋势。同时,也提供了在线交流平台,方便学生之间互相讨论、分享学习心得。 项目源码示例(简化版,Python) 以下是一个简单的Python脚本示例,用于生成包含选择题和答案的模拟试题: python import random # 定义选择题题库 questions = [ {"question": "Python的创始人是谁?", "options": ["A. 林纳斯·托瓦兹", "B. 巴纳姆", "C. 比尔·盖茨", "D.

    基于Django的食堂点餐系统

    基于 MySQL+Django 实现校园食堂点餐系统。 主要环境: PowerDesigner MySQL Workbench 8.0 CE Python 3.8 Django 3.2.8 BootStrap 3.3.7 Django-simpleui

    基于SpringBoot的同城宠物照看系统源码数据库文档.zip

    基于SpringBoot的同城宠物照看系统源码数据库文档.zip

    value_at_a_point.ipynb

    GEE训练教程

    基于springboot+Web的心理健康交流系统源码数据库文档.zip

    基于springboot+Web的心理健康交流系统源码数据库文档.zip

    kotlin 实践微信插件助手, 目前支持抢红包(支持微信最新版本 7.0.0及7.0.3).zip

    微信小程序 kotlin 实践微信插件助手, 目前支持抢红包(支持微信最新版本 7.0.0及7.0.3).zip

    N32G45X运放电路检测电压

    N32G45X运放电路检测电压

    梦幻西游道人20241121数据

    梦幻西游道人是梦幻西游里面的一个NPC,主要是刷全服最实惠的高级兽决和其他很好用的比较贵的东西,在长安城、傲来国、长寿村中的任意一个场景出现,一般会出现30分钟,不过东西一般都被秒刷。 梦幻西游道人出现时间解析如下: 1.梦幻西游道人出现时间一直都保持着一年出现两次的规律,即2、3月份的元宵节期间来一次,9月份的教师节期间出现一次。 2.云游道人每个整点(0:00至7:00不出现)会在长安城、傲来国、长寿村中的任意一个场景出现,每次出现后停留时间为30分钟。

    tables-3.7.0-cp38-cp38-win_amd64.whl

    tables-3.7.0-cp38-cp38-win_amd64.whl

    基于springboot旧物回收管理系统源码数据库文档.zip

    基于springboot旧物回收管理系统源码数据库文档.zip

    MariaDB集群部署手册word版最新版本

    MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB。 本文档介绍了MariaDB 10.1的集群部署,至少三台机器做成集群,每台可以同时提供读和写,感兴趣的小伙伴们可以参考一下

    JavaScript语言教程:基础语法、DOM操作、事件处理及新特性详解

    内容概要:本文档全面介绍了JavaScript作为一种轻量级的、解释型的语言及其在前端开发中的广泛应用。从JavaScript的基本概念出发,详尽讲解了基础语法(如变量、数据类型、运算符、流程控制)、函数和闭包、对象和原型、DOM操作(如获取、修改、添加和删除元素)、事件处理(如事件监听器、事件对象)、AJAX与Fetch API、ES6+的新特性(如箭头函数、模板字符串、解构赋值)以及前端框架和库(React、Vue、Angular)。除此之外,文章还涉及了代码优化技巧(如减少DOM操作、选择适当的算法和数据结构、使用工具提升代码性能),并对JavaScript的应用场景和发展趋势进行了展望。 适用人群:适用于初学者或具有少量编程经验的学习者,旨在帮助他们系统掌握JavaScript基础知识和前沿技术。 使用场景及目标:通过本教程的学习,读者不仅可以学会基本语法,还能理解并掌握高级概念和技术,如DOM操纵、事件处理机制、异步编程及最新的ECMAScript规范。这不仅有助于改善用户体验、增强网站互动性和响应速度,也能有效提升自身的编码水平和项目开发能力。 其他说明:此文档不仅涵盖了JavaScript的传统功能,还有现代前端技术和最佳实践指导,确保读者能够紧跟行业发展步伐,成为合格甚至优秀的Web开发人员。

    毕业设计&课设_安卓公交线路查询 app(含架构技术、数据格式及数据库相关说明).zip

    该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

    基于springboot高考志愿智能推荐系统源码数据库文档.zip

    基于springboot高考志愿智能推荐系统源码数据库文档.zip

    经典-FPGA时序约束教程

    经典-FPGA时序约束教程

    mcu交互实验整体文件

    mcu交互实验整体文件

    Collins COBUILD (CN).mdx

    Collins COBUILD (CN).mdx

    自定义springboot starter,提供HelloService

    自定义springboot starter

Global site tag (gtag.js) - Google Analytics